TGViewer
C++ geek C++ geek @cpp_geek · 3.53K subscribers
Post #415 1.22K
🧬 Двойная цена std::shared_ptr: Почему профи всегда пишут make_shared?

Мы все используем умные указатели. Но то, как вы их создаете, кардинально меняет работу с памятью под капотом.

Встречали такой код?

// 🐢 ПЛОХО: Классический подход
std::shared_ptr<User> user(new User());


Кажется, всё логично: выделили память через new, передали в shared_ptr. Но на деле вы заставляете программу сделать две аллокации (выделения памяти) вместо одной.

⚙️ Анатомия shared_ptr

std::shared_ptr состоит из двух частей:

1. Сам объект (ваши данные User).

2. Контрольный блок (Control Block) - служебная структура, где лежат счетчики ссылок (reference count) и счетчики weak_ptr.

Когда вы пишете std::shared_ptr<User>(new User()), происходит следующее:

1. Отрабатывает new User() - программа идет к ОС и просит кусок памяти.

2. Конструктор shared_ptr видит сырой указатель, понимает, что ему нужен Контрольный блок, и еще раз идет к ОС за вторым куском памяти.

Два системных вызова. Фрагментация кучи (heap). Промахи кэша процессора, потому что объект и счетчик лежат в разных концах памяти.



🚀 Решение: std::make_shared


// 🚀 ХОРОШО: Единый блок памяти
auto user = std::make_shared<User>();


Что делает make_shared? Он считает размер вашего объекта User + размер Контрольного блока, и просит у операционной системы один большой кусок памяти за один раз.

Плюсы:
• В 2 раза меньше аллокаций. Код работает быстрее.

• Cache Locality. Объект и счетчик ссылок лежат в памяти впритык друг к другу. Процессор это обожает.

• Безопасность. До C++17 старый подход с new мог привести к утечке памяти, если функция принимала несколько аргументов и один из них бросал исключение. С make_shared это исключено.

🦇 Темная сторона (О чем не пишут в туториалах)

Есть ровно один случай, когда make_shared может навредить. Это связано со слабыми указателями (std::weak_ptr).

Если вы удалили все shared_ptr, вызывается деструктор объекта User. Но если остался хотя бы один weak_ptr, Контрольный блок обязан жить!
А так как make_shared склеил Контрольный блок и объект в один кусок памяти, оперативная память из-под объекта User не вернется системе, пока жив weak_ptr (даже если сам объект уже "мертв" и деструктор отработал).
Если ваш объект весит 500 Мегабайт - вы получите «фантомную» утечку памяти.

💡В 99% случаев используйте std::make_shared. Используйте new std::shared_ptr только если у вас гигантские объекты, на которые подолгу смотрят «зависшие» weak_ptr, или если вам нужен кастомный удалитель (custom deleter).

#cpp #memory #pointers #optimization #sharedptr #coding #tips

➡️ @cpp_geek
  • 👍 5
  • ❤ 1
More from @cpp_geek
  1. Sep 24, 2026🎥 Вебинар по C++: Паттерн многопоточного программирования «Producer-Consumer» Когда неско…
  2. Sep 24, 2026Execution policy для параллельных алгоритмов Execution policy в C++ — это новшество, введе…
  3. Sep 17, 2026В чем разница между git fetch и git pull? Разница между этими командами заключается в том,…
  4. Sep 15, 2026std::tie std::tie — это функция, которая создает кортеж ссылок на lvalue из своих аргумент…
  5. Sep 11, 2026move constructor Move-конструктор — это специальный конструктор, который позволяет эффекти…
  6. Sep 9, 2026Move-only объекты и почему std::unique_ptr нельзя копировать Многие удивляются, когда комп…
Threads Profile ViewerView any public Threads profile without an account.Open ThreadLook →Writing with AI? Make it sound human.Metric37 rewrites AI drafts so they read naturally. Free AI detector, 1,500 words free.Try Metric37 →