2012-03-20 3 views
8

У меня есть двойной массив, выделенный указателем на указатель.Двойной (двумерный) массив с использованием std :: unique_ptr

// 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. Что я здесь делаю неправильно?

ответ

2

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

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

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

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

+0

Спасибо за ваши данные. То, как я понимаю unique_ptr, заключается в том, что он гарантирует, что экземпляр, на который он указывает, имеет только 1 ссылку. Поэтому, используя unique_ptr, чтобы указать на unique_ptr для создания матрицы, должно быть хорошо использовать unique_ptr, учитывая, что не будет другой ссылки на экземпляр. Кроме того, я не понимаю причины последнего предложения. Благодарю. – Evan

+0

любой класс RAII должен предложить ту же уникальную гарантию. Большинство классов C++ - это RAII. Поэтому вы должны использовать правильный инструмент для работы. вектор и массив должны быть предпочтительнее unique_ptr здесь. –

+0

Причина для уникальных ptrs состоит в основном для размещения динамически выделенных отдельных объектов. Я не могу сразу вспомнить причину хранения массива в unique_ptr. –

15

Вы не можете назначить 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; 

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

+1

Конечно, 'std :: unique_ptr (новый int [2]);' будет иметь неправильный deleter - это должно быть 'std :: unique_ptr (new int [2]);'. Упреждающий +1 при условии, что вы это исправите. ; -] – ildjarn

+0

@ildjarn: Я никогда не использовал 'unique_ptr' массивов, я нечеткий по синтаксису. Благодаря! –

+0

Спасибо за ваши данные! – Evan

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

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

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

// лет

1

Единственные причины я могу думать, чтобы использовать зЬй :: unique_ptr (или говорят, повышение :: scoped_array) по станд :: вектор для удерживающих массивов обычно не применяется ...

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

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

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

+0

Преимущество unique_ptr над вектором заключается в том, что вы можете избежать инициализации, стоимость которой может быть значительной в некоторых случаях. Ссылка http://stackoverflow.com/questions/96579/stl-vectors-with-uninitialized-storage и http://stackoverflow.com/questions/7546620/operator-new-initializes-memory-to-zero – goertzenator

4

Если вы не роскошь использования 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 позаботится о выделении строки размерности массива. Конечный () в new int*[10]() гарантирует, что каждый указатель столбца инициализируется до nullptr.

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

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

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

+0

Может ли это быть расширен с использованием '_mm_malloc' и как? –

+0

Используйте '__mm_malloc', чтобы выделить память и использовать' _mm_free' в пользовательском удалении. – sakra

+0

Спасибо, я получил эту работу. Забыл вторую часть, т. Е. Выделил отдельные строки (без этого я получил ошибку сегментации). Работа над примером с использованием вариативных шаблонов, где я могу создавать многомерные массивы, без доступа к данным с помощью '.get()'. –

0

Пример дальше вдохновил меня для этого решения

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]); 
} 

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