Многомерные массивы C++ в куче

Как бы я пошел о динамическом распределении многомерного массива?

12 ответов


Если вы уже знаете размер вложенных измерений, вы также можете буквально выделить многомерный массив, используя new:

typedef int dimensions[3][4];

dimensions * dim = new dimensions[10];
dim[/* from 0 to 9 */][/* from 0 to 2 */][/* from 0 to 3 */] = 42;
delete [] dim;

вместо 10, может быть передано значение, определенное во время выполнения. Поскольку он не является частью оператора типа new, это разрешено. Это хорошо, если вы знаете количество столбцов, но хотите сохранить переменное количество строк, например. Typedef упрощает чтение кода.


для полноты, вот лучший способ сделать это на C++, когда вы заранее знаете границы массива. Преимущество использования следующего класса заключается в том, что вам не нужно беспокоиться о вызове delete[] для ваших данных. Это означает, что этот класс будет безопасным для исключений, и все остальные отличные вещи о RAII.

template<typename T, int width, int height>
class MultiArray
{
    private:
        typedef T cols[height];
        cols * data;
    public:
        T& operator() (int x, int y) { return data[x][y]; }
        MultiArray() { data = new cols[width]; }
        ~MultiArray() { delete [] data; }
};

использование:

MultiArray<int, 10, 10> myArray;
myArray(2, 3) = 4;
cout << myArray(2, 3);

edit: и, пока я на нем, вот настройка, которую вы можете использовать, если вы не знать границы массива до выполнения:

template<typename T>
class Array2D
{
    private:
        const int width;
        T * data;
    public:
        T& operator() (int x, int y) { return data[y*width + x]; }
        Array2D(const int w, const int h) : width(w) { data = new T[w*h]; }
        ~Array2D() { delete [] data; }
};

использование:

Array2D myArray(10, 10);
myArray(3, 4) = 42;
cout << myArray(3, 4);

Как насчет использования Boost.Multiarray ? Я считаю, что это вполне отвечает вашим потребностям ! http://www.boost.org/doc/libs/1_37_0/libs/multi_array/doc/user.html#sec_introduction

вот выдержка из страницы документации:

 #include < boost/multi_array.hpp >

 #include < cassert >

int main () 

{

  // Create a 3D array that is 3 x 4 x 2

  typedef boost::multi_array< double, 3 > array_type;

  typedef array_type::index index;

  array_type A(boost::extents[3][4][2]);


  // Assign values to the elements

  int values = 0;

  for(index i = 0; i != 3; ++i) 

    for(index j = 0; j != 4; ++j)

      for(index k = 0; k != 2; ++k)

        A[i][j][k] = values++;

  // Verify values

  int verify = 0;

  for(index i = 0; i != 3; ++i) 

    for(index j = 0; j != 4; ++j)

      for(index k = 0; k != 2; ++k)

        assert(A[i][j][k] == verify++);

  return 0;

}

посмотреть этот: C++ FAQ по Marshall Cline

см. " как выделить многомерные массивы с помощью new?"и", Но код предыдущего FAQ является очень сложным и подверженным ошибкам! Разве нет более простого способа?" разделы.


std::vector<std::vector<int> >следует упомянуть, так как это часто самый простой способ. Однако имейте в виду, что он не прямоугольный. Не каждый std::vector<int> должна иметь одинаковую длину.


Я удивлен, что никто не упомянул boost::multi_array еще. Мне нужен был 2D-массив в программе только на прошлой неделе, и оказалось, что это намного проще и быстрее кодировать, чем домашние решения, которые я придумал раньше (все из которых упоминаются в других комментариях).


вот реализация, которую я получил; я объявляю один непрерывный блок ints вместо создания новых блоков внутри моего цикла for, поэтому я не вызываю ошибок страницы повсюду. Спасибо eJames за указание на то, почему этот код был нарушен изначально.

int width = 10, height = 10, totalSize = width*height;
int **myArray = new int*[width];
int *data = new int[totalSize];

for ( int i = 0; i < height; ++i )
{
    myArray[i] = data + (i*width);
}

// do some things here

delete[] data;
delete[] myArray;

ваш цикл не будет записывать значения указателя в myArray правильно. Я бы предложил следующее:

int width = 10;
int height = 10;
int ** myArray = new int*[width];
int * data = new int[width*height];
int * index = data;
for (int i = 0; i < width; i++)
{
    myArray[i] = index;
    index += height;
}

// ...

delete[] data;
delete[] myArray;

в качестве другой альтернативы,STLSoft включает в себя fixed_array_2d класс (а также 3D и 4D версии). По сравнению с доморощенными решениями, приведенными здесь, он имеет аналогичную реализацию, но более полный набор функций(полная поддержка итераторов и т. д.). По сравнению с boost:: multi_array, это легче и проще на не совсем совместимых компиляторах C++, но (намеренно) не хватает некоторых функций multi_array.


вы можете индексировать одномерное как 2, 3 или n мерное, если вы просто пробел над правильным количеством элементов. Например, если у меня есть 10 строк и 10 столбцов, я знаю, что если я нахожусь в строке 3, мне придется пройти по крайней мере 30 элементов, чтобы добраться до нее.

почему-то я предпочитаю эту нотацию для простых 2D-массивов, так как мне не нужно беспокоиться о вложенных уровнях указателей. Недостатком является нотация индекса messier. Вот пример с 2D-массивом с n строками и m колонки:

int *matrix = new int[n*m];

//set element (3,7) to 10
matrix[3*m+7] = 10;

//print the matrix
for (int i = 0; i < n; i++) {
  for (int j = 0; j < m; j++) {
    cout << matrix[i*m+j] << ' ';
  }
  cout << '\n';
}

У меня вопрос здесь охватывает в основном ту же тему довольно хорошо благодаря некоторым превосходным ответам. Однако он не охватывает N-мерные массивы, которые я также не видел в ответах здесь, но это было бы полезно.


это воспроизведение сообщения в другом потоке. Он делает именно то, что вы хотите, без необходимости заранее знать размеры массива и без использования boost или STL.

Heres процедура, которая выделяет 3D-массив размерности N1 x N2 x N3 в непрерывном пространстве памяти, позволяя вам синтаксис a[i][j][k] для доступа к оператору. Массив динамический, но непрерывный, поэтому это огромный плюс над векторным подходом и циклами новых[] звонки.

template <class T> T ***Create3D(int N1, int N2, int N3)
{
    T *** array = new T ** [N1];

    array[0] = new T * [N1*N2];

    array[0][0] = new T [N1*N2*N3];

    int i,j,k;

    for( i = 0; i < N1; i++) {

        if (i < N1 -1 ) {

            array[0][(i+1)*N2] = &(array[0][0][(i+1)*N3*N2]);

            array[i+1] = &(array[0][(i+1)*N2]);

        }

        for( j = 0; j < N2; j++) {     
            if (j > 0) array[i][j] = array[i][j-1] + N3;
        }

    }

    cout << endl;
    return array;
};

template <class T> void Delete3D(T ***array) {
    delete[] array[0][0]; 
    delete[] array[0];
    delete[] array;
};

и позже в вашей рутине реализации...

int *** array3d;
int N1=4, N2=3, N3=2;

int elementNumber = 0;

array3d = Create3D<int>(N1,N2,N3);

//equivalently, a 'flat' array could be obtained with
//int * array = array3d[0][0];

cout << "{" << endl;
for (i=0; i<N1; i++) {
    cout << "{";
    for (j=0; j<N2; j++) {
        cout << "{";
        for (k=0; k<N3; k++) {
            array3d[i][j][k] = elementNumber++;
            cout << setw(4) << array3d[i][j][k] << " ";

            //or if you're using the flat array:
            //array[i*N2*N3 + j*N3 + k] = elementNumber++;

        }
        cout << "}";
    }
    cout << "}";
    cout << endl ;
}
cout << "}" << endl;

Delete3D(array3d);

дает на выходе:

{
{{   0    1 }{   2    3 }{   4    5 }}
{{   6    7 }{   8    9 }{  10   11 }}
{{  12   13 }{  14   15 }{  16   17 }}
{{  18   19 }{  20   21 }{  22   23 }}
}