Какие плюсы и минусы у std::make_shared?

«Какие плюсы и минусы у std::make_shared?» — вопрос из категории Управление памятью, который задают на 25% собеседований C/C++ Разработчик. Ниже — развёрнутый ответ с разбором ключевых моментов.

Ответ

std::make_shared — это функция-хелпер для создания std::shared_ptr. Её ключевое отличие от прямого использования конструктора shared_ptr — в оптимизации выделения памяти.

Плюсы:

  1. Безопасность при исключениях: Выделение памяти под объект и под control block (счётчик ссылок) происходит атомарно. Это исключает утечку памяти, если, например, new успешен, но конструктор shared_ptr бросает исключение.
  2. Эффективность: Обычно требует одного выделения памяти (heap allocation) для размещения и объекта, и control block вместе. Это уменьшает фрагментацию и накладные расходы.
  3. Локализация данных: Объект и control block находятся рядом в памяти, что может улучшить производительность кэша процессора.
  4. Удобство: Более лаконичный синтаксис.

Минусы:

  1. Невозможность использовать кастомный аллокатор: make_shared всегда использует operator new. Для использования аллокатора нужно применять allocate_shared.
  2. Невозможность передать пользовательский deleter.
  3. Продлённое время жизни памяти объекта: Память, занятая объектом, освобождается только тогда, когда уничтожены все shared_ptr и weak_ptr, ссылающиеся на него, так как объект и control block находятся в одном блоке памяти. При использовании shared_ptr(new T) память под объект освобождается сразу при уничтожении последнего shared_ptr, даже если живут weak_ptr.

Пример:

// Хорошо: одно выделение, безопасно при исключениях.
auto sp1 = std::make_shared<MyClass>(arg1, arg2);

// Потенциально проблемно: два выделения (new + control block).
// Если 'new MyClass' успешен, но выделение под control block бросит bad_alloc,
// память под MyClass утечёт.
std::shared_ptr<MyClass> sp2(new MyClass(arg1, arg2));