2015-05-28 3 views
8

[Followup на this вопрос]Инициализация shared_ptr <T> из unique_ptr <T[]>

Я занимаюсь немного со смарт-указатели на массивы с стиле в последнее время. Я в конечном итоге закончил делать рекомендуемую вещь и вместо этого использовал интеллектуальные указатели для векторов, но в течение этого периода я получил немного советов: не используйте объект shared_ptr<T> для управления массивом, изначально сделанным с make_unique<T[]>, потому что он не будет звонить delete[] а скорее delete.

Это не кажется логичным для меня, и я проверил как Coliru и стандарт:


Этот код:

#include <iostream> 
#include <memory> 

int main() 
{ 
    std::cout << "start!\n"; 
    auto customArrayAllocator = [](unsigned int num){ 
     std::cout << "custom array allocator\n"; 
     return new int[num]; 
    }; 

    std::cout << "allocator constructed\n"; 

    auto customArrayDeleter = [](int *ptr){ 
     std::cout << "custom array deleter\n"; 
     delete[] ptr; 
    }; 

    std::cout << "deleter constructed\n"; 

    std::unique_ptr<int[], decltype(customArrayDeleter)> 
     myUnique(customArrayAllocator(4), customArrayDeleter); 

    std::cout << "unique_ptr constructed\n"; 

    std::shared_ptr<int> 
     myShared = std::move(myUnique); 

    std::cout << "shared_ptr constructed\n"; 
} 

производит этот выход:

start! 
allocator constructed 
deleter constructed 
custom array allocator 
unique_ptr constructed 
shared_ptr constructed 
custom array deleter 

Который, по-видимому, указывает, что делетер unique_ptr<T[]> передан в shared_ptr<T>, как я и ожидал.


от стандартного § С ++ 14 20.8.2.2.1 pg. 571 of doc, 585 of pdf

шаблон shared_ptr (unique_ptr & & г);
Примечание: Этот конструктор не должен участвовать в разрешении перегрузки, если уникальный_ptr :: указатель не может быть конвертирован в T *.
Эффекты: Эквивалент shared_ptr (r.release(), r.get_deleter()), когда D не является ссылочным типом, иначе shared_ptr (r.release(), ref (r.get_deleter())).
Безопасность исключений: Если выбрано исключение, конструктор не имеет эффекта.

Если я читаю это право, это означает, что shared_ptr объект строит себя как от указателя и Deleter в виде unique_ptr. Кроме того, я понимаю (от ответа на исходный вопрос), что тип unique_ptr<T[]> - T*, который должен быть конвертирован в shared_ptr<T>::pointer's T*. Таким образом, отладчик должен быть скопирован прямо из объекта unique_ptr, верно?


ли мой тест работает только потому, что это на самом деле не эквивалентна функции std::make_shared<T[]> или синтаксис

std::shared_ptr<T> mySharedArray = std::make_unique<T[]>(16); 

хорошее, исключение безопасно (и чище) альтернатива

std::shared_ptr<T> mysharedArray(new T[16], [](T* ptr){delete[] ptr;}); 

и т. Д., Когда я не могу использовать Boost shared array и желаю избегать включения либо vector, либо array hea с моим кодом?

+1

N.B. хотя массив гарантированно будет удален, существует потенциальная проблема безопасности при преобразовании 'unique_ptr ' -> 'shared_ptr ' -> 'shared_ptr ', см. http://stackoverflow.com/q/32483375/981959 –

ответ

6

Да, ваш пример действителен по тем причинам, которые вы указали. unique_ptr::pointer - int *, и вы пытаетесь передать право собственности на это на shared_ptr<int>, поэтому преобразованный конструктор, который вы указали, будет участвовать в разрешении перегрузки и сделает копию делетера (std::default_delete<int[]>), потому что это не ссылочный тип.

Так справедливо следующее, и будет вызывать delete[] когда shared_ptr счетчик ссылок стремится к нулю

std::shared_ptr<T> mySharedArray = std::make_unique<T[]>(16); 

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

std::shared_ptr<T> mySharedArray(new T[16], std::default_delete<int[]>()); 

, что приведет к тому, что mySharedArray приобретет тот же самый делетер, что и предыдущая строка.

Смежные вопросы