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>>
,