Std:: shared_ptr инициализация: make_shared <Foo>() vs shared_ptr <T> (новый Foo)
Какая разница между:
std::shared_ptr<int> p = std::shared_ptr<int>( new int );
и
std::shared_ptr<int> p = std::make_shared< int >();
?
Какой из них я должен предпочесть и почему?
Р. S. Довольно уверен, что на это, должно быть, уже был дан ответ, но я не могу найти аналогичный вопрос.
Ответы
Ответ 1
Оба примера являются более подробными, чем необходимо:
std::shared_ptr<int> p(new int); // or '=shared_ptr<int>(new int)' if you insist
auto p = std::make_shared<int>(); // or 'std::shared_ptr<int> p' if you insist
Какая разница?
Основное отличие состоит в том, что для первого требуется два распределения памяти: один для управляемого объекта (new int
) и один для подсчета ссылок. make_shared
должен выделять один блок памяти и создавать в нем.
Какой из них я должен предпочесть и почему?
Обычно вы используете make_shared
, поскольку он более эффективен. Как отмечено в другом ответе, он также избегает любой возможности утечки памяти, поскольку у вас никогда не было необработанного указателя на управляемый объект.
Однако, как отмечается в комментариях, у него есть потенциальный недостаток: память не будет выпущена при уничтожении объекта, если есть все еще слабые указатели, препятствующие удалению общего счетчика.
Ответ 2
Из en.cppreference.com
В отличие от декларации std::shared_ptr<T> p(new T(Args...))
выполняется как минимум два распределения памяти, которые могут повлечь ненужные служебные данные.
Кроме того, f(shared_ptr<int>(new int(42)), g())
может привести к утечке памяти, если g выбрасывает исключение. Эта проблема не существует, если используется make_shared.
Поэтому я бы рекомендовал подход make_shared
, если это возможно.
Ответ 3
Помните, что make_shared
ограничивает использование функций распределения/освобождения по умолчанию, поэтому, если вы хотите иметь больше контроля, make_shared
не является опцией. Другими словами, что-то вроде
std::shared_ptr<uint8_t>(p, [](uint8_t *p){ /*user code */});
невозможно с помощью make_shared
. Вместо этого можно использовать allocate_shared
, но может быть указан только распределитель, а не дебетер. Иногда нужно управлять распределением и удалением обернутого класса.