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

Просто небольшой запрос относительно shared_ptr.

Хорошо ли использовать shared_ptr для массива? Например,

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

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

Ответ 1

По умолчанию 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[] при уничтожении управляемого объекта.


Как показано в комментариях, с С++ 11 вы можете использовать std::default_delete частичную специализацию для типов массивов вместо array_deleter выше.

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[]


Изменения, внесенные расширениями С++ для фундаментальных основ библиотеки

shared_ptr дополняется Технической спецификацией основных средств библиотеки (TS), чтобы позволить ей работать из коробки для случаев, когда ей принадлежит массив объектов; нет необходимости явно указывать дебетер. Текущая черновик изменений 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*.

Ответ 2

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