Двойной (двумерный) массив с использованием std :: unique_ptr - PullRequest
10 голосов
/ 21 марта 2012

У меня есть двойной массив, выделенный указателем на указатель.

  // pointer to pointer
  int **x = new int *[5];   // allocation
  for (i=0; i<5; i++){
      x[i] = new int[2];
  }

  for (i=0; i<5; i++){      // assignment
      for (j=0; j<2; j++){
          x[i][j] = i+j;
      }
  }

  for (i=0; i<5; i++)   // deallocation
      delete x[i];
  delete x;

Я пытаюсь сделать это, используя unique_ptr:

std::unique_ptr<std::unique_ptr<int>[]> a(new std::unique_ptr<int>[5]);
  for (i=0; i<5; i++)
      a[i] = new int[2];

, но получаю сообщение об ошибке, сообщающее, чтоno operator = matches these operands.Что я тут не так делаю?

Ответы [ 7 ]

16 голосов
/ 21 марта 2012

Вы не можете назначить int* для std::unique_ptr<int[]>, что является причиной вашей ошибки.Правильный код:

      a[i] = std::unique_ptr<int[]>(new int[2]);

Тем не менее, piokuc верен, поэтому очень редко можно использовать unique_ptr для массивов, поскольку именно для этого нужны std::vector и std::array, в зависимости от того,известен заранее.

//make a 5x2 dynamic jagged array, 100% resizable any time
std::vector<std::vector<int>> container1(5, std::vector<int>(2)); 
//make a 5x2 dynamic rectangular array, can resize the 5 but not the 2
std::vector<std::array<2, int>> container1(5); 
//make a 5x2 automatic array, can't resize the 2 or 5 but is _really fast_.
std::array<5, std::array<2, int>> container;

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

5 голосов
/ 23 февраля 2015

Если вы не можете позволить себе роскошь использовать std::array или std::vector вместо динамически размещаемого массива, вы можете использовать unique_ptr для двумерного массива в C ++ 11 следующим образом:

std::unique_ptr<int*, std::function<void(int**)>> x(
    new int*[10](),
    [](int** x) {
        std::for_each(x, x + 10, std::default_delete<int[]>());
        delete[] x;
    }
);

Объявление unique_ptr обеспечивает распределение размера массива row .Конечный () в new int*[10]() гарантирует, что каждый указатель столбца инициализируется в nullptr.

A для цикла, а затем выделяет массивы столбцов:

for (size_t row = 0; row < 10; ++row) {
    (x.get())[row] = new int[5];
}

Когда unique_ptrвыходит из области видимости, его пользовательская лямбда-функция удаления удаляет массивы столбцов перед удалением массива строк.В выражении for_each используется функтор default_delete .

2 голосов
/ 21 марта 2012
for (i=0; i<5; i++)   // deallocation
      delete x[i];
  delete x;

НЕТ НЕТ НЕТ НЕТ

delete [] x[i];
delete [] x;

// йо

2 голосов
/ 21 марта 2012

Ваш код эффективно манипулирует массивом массивов типа int.

В C ++ вы обычно хотели бы реализовать его как:

std::vector<std::vector<int> > x;

Это не очень хороший случай для unique_ptr.Кроме того, вам не нужно использовать указатели на unique_ptr и динамически размещать объекты unique_ptr.Весь смысл unique_ptr состоит в том, чтобы исключить использование указателей и обеспечить автоматическое распределение и освобождение объектов.

1 голос
/ 25 февраля 2013

Единственные причины, по которым я могу использовать std :: unique_ptr (или сказать boost :: scoped_array) над std :: vector для хранения массивов, обычно не применимы ...

1) это сохраняет 1или 2 указателя памяти, в зависимости от того, знаете ли вы, какой размер всех массивов [не имеет значения, если у вас нет огромного количества очень маленьких массивов]

2), если вы просто передаете массив внекоторая функция, которая ожидает массив в стиле C или необработанный указатель, может выглядеть более естественно.std :: vector гарантированно находится в последовательном хранилище, поэтому передача (a.empty() ? nullptr : &a[0], a.size()) в такую ​​функцию также допустима на 100%.

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

0 голосов
/ 11 сентября 2018
for (i=0; i<5; i++)   // deallocation
      delete x[i];
delete x;

Это распространенная ошибка здесь.x [i] - массив, поэтому сначала вы должны удалить каждый массив, используя delete []. Затем вы можете удалить свой массив int *, используя delete []

. Правильное удаление будет:

for (i=0; i<5; i++)   
      delete[] x[i]; //desallocate each array of int
delete[] x; //desallocate your array of int*
x = nullptr; //good practice to be sure it wont cause any dmg
0 голосов
/ 13 октября 2016

Еще один пример вдохновил меня на это решение

size_t k = 10;
std::unique_ptr<int*, std::function<void(int**)>> y(new int*[k](),
    [](int** x) {delete [] &(x[0][0]);
                 delete[] x;});

// Allocate the large array
y.get()[0] = new int[k*10];

// Establish row-pointers
for (size_t row = 0; row < k; ++row) {
  (y.get())[row] = &(y.get()[0][0]);
}

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

...