Как правильно объявлять и инициализировать (большой) массив двумерных объектов в C++?

Мне нужно создать большой двумерный массив объектов. Я читал некоторые связанные вопросы на этом сайте и другие, касающиеся multi_array, matrix, vector и т. Д., Но не смог собрать их вместе. Если вы рекомендуете использовать один из них, пожалуйста, переведите код ниже.

Некоторые соображения:

  • Массив несколько большой (1300 х 1372).
  • Я мог бы работать с более чем одним из них одновременно.
  • Я должен передать его функции в какой-то момент.
  • Скорость это большой фактор.

Два подхода, о которых я подумал:

Pixel pixelArray[1300][1372];
for(int i=0; i<1300; i++) {
    for(int j=0; j<1372; j++) {
        pixelArray[i][j].setOn(true);
        ...
    }
}

а также

Pixel* pixelArray[1300][1372];
for(int i=0; i<1300; i++) {
    for(int j=0; j<1372; j++) {
        pixelArray[i][j] = new Pixel();
        pixelArray[i][j]->setOn(true);
        ...
    }
}

Какой правильный подход / синтаксис здесь?

Редактировать:

Несколько ответов предположили Pixel маленький - я оставил подробности о Pixel для удобства, но это не маленький / тривиальный. Он имеет ~20 членов-данных и ~16 функций-членов.

6 ответов

Решение

Ваш первый подход распределяет все по стеку, что в целом хорошо, но приводит к переполнению стека, когда вы пытаетесь выделить слишком много стека. В современных ОС ограничение обычно составляет около 8 мегабайт, поэтому размещение массивов из 1300 * 1372 элементов в стеке невозможно.

Ваш второй подход выделяет 1300 * 1372 элементов в куче, что является огромной нагрузкой для распределителя, который содержит несколько связанных списков с частями выделенной и свободной памяти. Также плохая идея, тем более что Pixel кажется довольно маленькой.

Что бы я сделал, это:

Pixel* pixelArray = new Pixel[1300 * 1372];
for(int i=0; i<1300; i++) {
    for(int j=0; j<1372; j++) {
        pixelArray[i * 1372 + j].setOn(true);
        ...
    }
}

Таким образом, вы выделяете один большой кусок памяти в куче. Стек доволен, как и распределитель кучи.

Я не уверен, насколько сложен ваш тип данных Pixel, но, возможно, что-то подобное будет работать для вас?:

std:: fill (массив, массив +100, 42); // устанавливает каждое значение в массиве на 42

Ссылка: Инициализация нормального массива с одним значением по умолчанию

Если вы хотите передать это функции, я бы проголосовал против использования простых массивов. Рассматривать:

void doWork(Pixel array[][]);

Это не содержит никакой информации о размере. Вы можете передать информацию о размере через отдельные аргументы, но я бы предпочел использовать что-то вроде std::vector. Конечно, для этого необходимо определить соглашение об адресации (основной ряд или основной столбец).

Альтернативой является std::vector>, где каждый уровень векторов представляет собой одно измерение массива. Преимущество: двойной индекс, как в pixelArray[x][y], работает, но создание такой структуры утомительно, копирование обходится дороже, потому что это происходит для отдельного экземпляра вектора вместо простого memcpy, а векторы, содержащиеся в вектор верхнего уровня не обязательно должен иметь одинаковый размер.

Это в основном ваши варианты использования стандартной библиотеки. Правильным решением будет что-то вроде std::vector с двумя измерениями. Вспоминаются числовые библиотеки и библиотеки манипулирования изображениями, но классы матриц и изображений, скорее всего, ограничены примитивными типами данных в своих элементах.

РЕДАКТИРОВАТЬ: Забыл прояснить, что все выше, это только аргументы. В конце концов, ваш личный вкус и контекст должны быть приняты во внимание. Если вы сами в проекте, вектор плюс определенное и задокументированное соглашение об адресации должно быть достаточно хорошим. Но если вы в команде, и, вероятно, кто-то не примет во внимание документированное соглашение, каскадная структура вектор-вектор, вероятно, будет лучше, потому что утомительные части могут быть реализованы вспомогательными функциями.

Проверьте Универсальную библиотеку изображений Boost.

gray8_image_t pixelArray;
pixelArray.recreate(1300,1372);
for(gray8_image_t::iterator pIt = pixelArray.begin(); pIt != pixelArray.end(); pIt++) {
    *pIt = 1;
}

Хотя я не обязательно делаю это структурой, это демонстрирует, как я буду подходить к хранению и доступу к данным. Если Pixel довольно большой, вы можете вместо этого использовать std::deque.

struct Pixel2D {
  Pixel2D (size_t rsz_, size_t csz_) : data(rsz_*csz_), rsz(rsz_), csz(csz_) {
    for (size_t r = 0; r < rsz; r++)
    for (size_t c = 0; c < csz; c++)
      at(r, c).setOn(true);
  }
  Pixel &at(size_t row, size_t col) {return data.at(row*csz+col);}
  std::vector<Pixel> data;
  size_t rsz;
  size_t csz;
};

Моим личным предпочтением было бы использовать std::vector

typedef  std::vector<Pixel>       PixelRow;
typedef  std::vector<PixelRow>    PixelMatrix;

PixelMatrix   pixelArray(1300, PixelRow(1372, Pixel(true)));
      //                 ^^^^           ^^^^  ^^^^^^^^^^^
      //                 Size 1         Size 2   default Value
Другие вопросы по тегам