C++ geek
3.61K subscribers
277 photos
5 videos
28 links
Учим C/C++ на примерах
Download Telegram
🧬 Двойная цена 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
👍51