Почему многомерный массив не может быть выделен одним новым вызовом в C ++? - PullRequest
5 голосов
/ 24 октября 2010

В C ++ вы можете легко выделить одномерный массив следующим образом:

T *array=new T[N];

И вы также можете удалить его одним оператором:

delete[] array;

Компилятор узнает магию какчтобы освободить правильное количество байтов.

Но почему вы не можете распределить 2-мерные массивы, как это?

T *array=new T[N,M];

Или даже так?

T *array=new T[N,M,L];

Если вы хотите многомерный, вы должны сделать это следующим образом:

T **array=new T*[N];
for(int i=0;i<N;i++) array[i]=new T[M];

Если вы хотите быструю программу, которая использует матрицы (матричные операции, алгоритмы собственных значений и т. Д.), Вы можете использоватькеш тоже для максимальной производительности, и это требует, чтобы данные были в одном месте.Использование vector<vector<T> > - та же самая ситуация.В C вы можете использовать массивы переменной длины в стеке, но вы не можете выделить их в куче (а пространство стека довольно ограничено), вы можете также делать массивы переменной длины в C ++, но они не будут присутствовать в C++ 0x.

Единственный обходной путь довольно хакерский и с ошибкой:

T *array=new T[N*M];
for(int i=0;i<N;i++)
   for(int j=0;j<M;j++)
   {
       T[i*N+j]=...;
   }

Ответы [ 8 ]

8 голосов
/ 24 октября 2010

Ваш обходной путь выполнения T *array=new T[N*M]; - самый близкий к настоящему многомерному массиву.Обратите внимание, что для размещения элементов в этом массиве вам нужно значение M (я полагаю, что ваш пример неверен, оно должно быть T[i*M+j]), которое известно только во время выполнения.

Когда вывыделить 2D-массив во время компиляции, скажем, array[5][10], значение 10 является константой, поэтому компилятор просто генерирует код для вычисления i*10+j.Но если вы сделали new T[N,M], выражение i*M+j зависит от значения M во время выделения массива.Компилятору понадобится какой-то способ сохранить значение M вместе с самим массивом, и отсюда все только запутается.Я думаю, именно поэтому они решили не включать такую ​​функцию в язык.

Что касается вашего обходного пути, вы всегда можете сделать его менее "хакерским", написав класс-обертку, который перегружает operator (), так чтоВы могли бы сделать что-то вроде array(i, j) = ....

3 голосов
/ 24 октября 2010

Поскольку многомерный массив - это нечто иное, чем массив массивов / указателей.

1 голос
/ 24 октября 2010

Почему многомерный массив не может быть выделен одним новым вызовом в C ++?

Потому что, когда ISO написал стандарт языка C ++, они не решили добавить эту функцию в язык. Я не знаю, почему они решили не делать этого.

Если вам это не нравится, вы можете создать вспомогательные функции для выделения / освобождения многомерных массивов или переключиться на язык, такой как C # или Java, который поддерживает простое размещение многомерных массивов.

1 голос
/ 24 октября 2010

использовать std :: vector

0 голосов
/ 22 июня 2011

Я бы порекомендовал вам использовать Boost :: multi_array из одноименной библиотеки, которая обеспечивает простой интерфейс для многомерного массива.Он может быть размещен в одной строке, и при достаточно высоком уровне оптимизации он обычно такой же быстрый, как и собственный массив.

Вот пример кода с сайта библиотеки:

#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;
}
0 голосов
/ 27 октября 2010

Я думал об этом вопросе вчера вечером, и это решение пришло ко мне.

T * raw = new T[N*M];
T ** array = new T*[N];

for(int i=0; i<N; i++)
    array[i] = raw + i * M;

Теперь «массив» действует так же, как непрерывный двухмерный массив статического размера.Вам просто нужно позаботиться об удалении как необработанного массива, так и многомерного массива.

0 голосов
/ 24 октября 2010

Однако вы можете выделить из кучи объект, содержащий двумерный массив. Я бы просто написал для него класс-оболочку.

0 голосов
/ 24 октября 2010

Поскольку запятая является оператором.

int a = (3, 5, 7, 9);

Программа оценит 3, откажется от результата, оцените 5, откажитесь от результата, оцените 7, откажитесь от результата, оцените 9 и присвойте его.

Следовательно, синтаксис, который вы ищете, не может быть использован, и сохранить обратную совместимость с c.

...