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, но может быть указан только распределитель, а не дебетер. Иногда нужно управлять распределением и удалением обернутого класса.