shared_ptr для массива: его следует использовать?

Просто небольшой вопрос относительно shared_ptr,

Это хорошая практика для использования shared_ptr указывая на массив? Например,

shared_ptr<int> sp(new int[10]);

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

2 ответа

Решение

С С ++ 17, shared_ptr может использоваться для управления динамически размещаемым массивом. shared_ptr аргумент шаблона в этом случае должен быть T[N] или же T[], Так что вы можете написать

shared_ptr<int[]> sp(new int[10]);

От n4659 [util.smartptr.shared.const]

  template<class Y> explicit shared_ptr(Y* p);

Требуется: Y должен быть полным типом. Выражение delete[] p, когда T это тип массива, или delete p, когда T не является типом массива, должен иметь четко определенное поведение и не должен генерировать исключения.
...
Замечания: когда T является типом массива, этот конструктор не должен участвовать в разрешении перегрузки, если выражение delete[] p хорошо сформирован и либо T является U[N] а также Y(*)[N] конвертируется в T*, или же T является U[] а также Y(*)[] конвертируется в T*,...

Чтобы поддержать это, тип члена element_type теперь определяется как

using element_type = remove_extent_t<T>;

Элементы массива могут быть доступны с помощью operator[]

  element_type& operator[](ptrdiff_t i) const;

Требуется: get() != 0 && i >= 0, Если T является U[N], i < N,...
Замечания: когда T не является типом массива, не указано, объявлена ​​ли эта функция-член. Если он объявлен, то не указано, каков его тип возврата, за исключением того, что объявление (хотя и не обязательно определение) функции должно быть правильно сформировано.


До C++17, shared_ptr не может использоваться для управления динамически размещенными массивами. По умолчанию, shared_ptr позвоню delete на управляемом объекте, когда на него больше нет ссылок. Тем не менее, когда вы выделяете с помощью new[] тебе нужно позвонить delete[], и не delete, чтобы освободить ресурс.

Для того, чтобы правильно использовать shared_ptr с массивом вы должны предоставить пользовательское средство удаления.

template< typename T >
struct array_deleter
{
  void operator ()( T const * p)
  { 
    delete[] p; 
  }
};

Создайте shared_ptr следующим образом:

std::shared_ptr<int> sp(new int[10], array_deleter<int>());

Сейчас shared_ptr правильно позвонит delete[] при уничтожении управляемого объекта.

Пользовательский удалитель выше может быть заменен

  • std::default_delete частичная специализация для типов массивов

    std::shared_ptr<int> sp(new int[10], std::default_delete<int[]>());
    
  • лямбда-выражение

    std::shared_ptr<int> sp(new int[10], [](int *p) { delete[] p; });
    

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

std::unique_ptr<int[]> up(new int[10]); // this will correctly call delete[]

Изменения, внесенные расширениями C++ для основ библиотеки

Еще одна альтернатива до C++17 перечисленным выше была предоставлена Технической спецификацией Основ библиотеки, которая дополнила shared_ptr чтобы позволить ему работать из коробки для случаев, когда он владеет массивом объектов. Текущий проект shared_ptr изменения, намеченные для этого TS, могут быть найдены в N4082. Эти изменения будут доступны через std::experimental пространство имен, и включены в <experimental/memory> заголовок. Несколько важных изменений в поддержке shared_ptr для массивов есть:

- определение типа члена element_type изменения

typedef T element_type;

 typedef typename remove_extent<T>::type element_type;

- Член operator[] добавляется

 element_type& operator[](ptrdiff_t i) const noexcept;

- В отличие от unique_ptr частичная специализация для массивов, оба shared_ptr<T[]> а также shared_ptr<T[N]> будет действительным, и оба приведут к delete[] вызывается на управляемом массиве объектов.

 template<class Y> explicit shared_ptr(Y* p);

Требуется: Y должен быть полным типом. Выражение delete[] p, когда T это тип массива, или delete p, когда T не является типом массива, должен быть правильно сформированным, иметь четко определенное поведение и не создавать исключений. когда T является U[N], Y(*)[N] должен быть конвертируемым в T*; когда T является U[], Y(*)[] должен быть конвертируемым в T*; иначе, Y* должен быть конвертируемым в T*,

Возможно, более простой альтернативой, которую вы могли бы использовать, является shared_ptr<vector<int>>,

Другие вопросы по тегам