C ++ Многомерные массивы в куче - PullRequest
22 голосов
/ 04 декабря 2008

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

Ответы [ 11 ]

24 голосов
/ 04 декабря 2008

Если вы уже знаете размер вложенных измерений, вы также можете буквально выделить многомерный массив, используя 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 облегчает чтение кода.

6 голосов
/ 06 декабря 2008

Как насчет использования 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;

}
6 голосов
/ 04 декабря 2008

Для полноты картины, вот лучший способ сделать это в 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);
6 голосов
/ 04 декабря 2008

Смотрите это: C ++ FAQ от Marshall Cline

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

3 голосов
/ 04 декабря 2008

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

3 голосов
/ 04 декабря 2008

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

3 голосов
/ 04 декабря 2008

Вот реализация, которую я получил; Я объявляю один непрерывный блок int s вместо создания новых блоков внутри моего цикла 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;
2 голосов
/ 06 декабря 2008

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

2 голосов
/ 04 декабря 2008

Ваш цикл не будет правильно записывать значения указателя в 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;
0 голосов
/ 05 марта 2013

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

Вот процедура, которая выделяет трехмерный массив измерения N1 x N2 x N3 в непрерывном пространстве памяти, в то же время предоставляя вам синтаксис [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 }}
}
...