Как умные указатели выбирают между удалением и удалением []? - PullRequest
33 голосов
/ 20 января 2012

Рассмотрим:

delete new std :: string [2];
delete [] new std :: string;

Все знают, что первая ошибка.Если бы второе не было ошибкой, нам не понадобилось бы два разных оператора.

Теперь рассмотрим:

std :: unique_ptr <int> x (new int [2]);
std :: unique_ptr <int> y (new int);

Знает ли x, что использовать delete[] вместо delete?


Предыстория: этот вопрос всплыл в моей голове, когда я подумал, что квалификация указателей типа массива будет удобной функцией языка.

int *[] foo = new int [2]; // OK
int *   bar = new int;     // OK
delete [] foo;             // OK
delete bar;                // OK
foo = new int;             // Compile error
bar = new int[2];          // Compile error
delete foo;                // Compile error
delete [] bar;             // Compile error

Ответы [ 4 ]

30 голосов
/ 20 января 2012

К сожалению, они не знают, какое удаление использовать, поэтому они используют delete. Вот почему для каждого умного указателя у нас есть аналог умного массива.

std::shared_ptr uses delete
std::shared_array uses delete[]

Итак, ваша линия

std :: unique_ptr <int> x (new int [2]);

фактически вызывает неопределенное поведение.

Кстати, если ты пишешь

std :: unique_ptr<int[]> p(new int[2]);
                     ^^

, тогда будет использоваться delete[], поскольку вы явно запросили это. Однако следующая строка будет UB.

std :: unique_ptr<int[]> p(new int);

Причина, по которой они не могут выбирать между delete и delete[], заключается в том, что new int и new int[2] точно такого же типа - int*.

Здесь относится к вопросу использования правильных удалителей в случае smart_ptr<void> и smart_ptr<Base>, когда Base не имеет виртуального деструктора.

6 голосов
/ 20 января 2012

Не существует "магического" способа определить, относится ли int* к:

  • единственное целое, выделенное для кучи
  • выделенный массив кучи
  • целое число в массиве, выделенном для кучи

Информация была потеряна системой типов, и никакой метод времени выполнения (переносимый) не может ее исправить. Это бесит и серьезный недостаток дизайна (*) в C, который унаследован от C ++ (некоторые говорят, ради совместимости).

Тем не менее, есть некоторые способы работы с массивами в умных указателях.

Во-первых, ваш unique_ptr тип неверен для работы с массивом, вы должны использовать:

std::unique_ptr<int[]> p(new int[10]);

что означает означает для вызова delete[]. Я знаю, что речь идет о реализации специального предупреждения в Clang для выявления очевидных несоответствий с unique_ptr: это проблема качества реализации (стандарт просто говорит, что это UB), и не все случаи могут быть рассмотрены без WPA.

Во-вторых, boost::shared_ptr может иметь пользовательское средство удаления, которое может , если вы настроите его для вызова правильного оператора delete[]. Тем не менее, есть boost::shared_array, специально предназначенный для этого. Еще раз, обнаружение несоответствий является проблемой качества реализации. std::shared_ptr страдает той же проблемой ( отредактировано после замечания ildjarn ).

Я согласен, что это не красиво. Это кажется настолько неприятным, что недостаток дизайна (*) от происхождения Си до сих пор преследует нас.

(*) некоторые скажут, что C сильно склоняется к тому, чтобы избежать накладных расходов, и это добавило бы накладные расходы. Я частично не согласен: malloc всегда знаю размер блока, в конце концов.

3 голосов
/ 20 января 2012

std::unique_ptr не предназначен для массива, поскольку я цитирую последний документ повышения:

Обычно shared_ptr не может правильно удерживать указатель на динамически распределяемый массив. См. shared_array для этого использования.

Если вы хотите управлять памятью для массива указателей, у вас есть несколько опций, зависящих от ваших требований:

  1. Использование boost::shared_array
  2. Использование std::vector из boost::shared_ptr
  3. Использовать контейнер указателя надбавки, например boost::ptr_vector
3 голосов
/ 20 января 2012

С Документация Microsoft :

(Частичная специализация unique_ptr<Type[]> управляет объектами массива, выделенными с помощью new[], и имеет средство удаления по умолчанию default_delete<Type[]>, специализированное для вызоваdelete[] _Ptr.)

Я добавил две последние квадратные скобки, похоже на опечатку, поскольку без них нет смысла.

...