З C ++ 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; });
Крім того, якщо вам дійсно не потрібне часткове управління над керованим об'єктом, a unique_ptr
краще підходить для цього завдання, оскільки він має часткову спеціалізацію для типів масивів.
std::unique_ptr<int[]> up(new int[10]); // this will correctly call delete[]
Зміни, внесені розширеннями C ++ для основ бібліотеки
Інша альтернатива попереднього C ++ 17 перерахованій вище була надана Технічною специфікацією "Основи бібліотеки" , яка доповнила її, shared_ptr
щоб дозволити їй працювати з коробки для випадків, коли їй належить масив об'єктів. Поточний проект shared_ptr
змін, передбачених для цієї ТС, можна знайти в 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*
.
std::vector
. Вам доведеться бути обережними, щоб передати масив навколо, використовуючи посилання, щоб не робити його копій. Синтаксис доступу до даних чистіший, ніж до спільного_ptr, і змінити розмір дуже легко. І ви отримуєте всю доброту STL, якщо хочете.