Но есть нюанс.
Представим такую систему.
1. Есть Большая Таблица в памяти, пусть для простоты это std::unordered_map с сотней миллионов записей. Таблица эта по сути сингл-тон и меняется редко.
2. Есть потоки, обрабатывающие запросы. Они используют эту таблицу. Чем быстрее поток выдаст ответ, тем лучше. Задержка с ответом весьма критична.
3. Доступ к таблице сделан так. Поток-обработчик вызывает функцию, которая получает нужные данные из таблицы. Эта функция вызывает глобальный геттер, возвращающий std::shared_ptr на таблицу, копия которого помещается в локальную переменную. Поработав с таблицей, функция вызовет деструктор своего shared_ptr и вернет результат.
4. Иногда эту таблицу нужно обновить. Для этого создаётся новая копия таблицы, и геттер будет возвращать указатель уже на неё. Когда все обращения к старой таблице закончатся (уничтожатся все shared_ptr), она автоматически освободится. Для этого геттер и возвращает именно shared_ptr, а raw pointer.
Здорово! В чём спрашивается, здесь подвох?
А подвох здесь в том, что деструктор этой структуры, из-за её объёмности, работает ужасно долго, и тот запрос, которому выпало последнему обратиться к старой таблице, подвиснет на критично большое время.
Решается проблема, конечно же, просто. Функция, обновляющая таблицу, сама создаёт копию shared_ptr, загружает новую таблицу, ждёт уменьшения счётчика до единицы, и уже потом вызывает деструктор. Тем самым она берёт удар на себя, а потоки-обработчики уже не запнутся об деструктор. Фактически, она содержит самый настоящий импровизированный сборщик мусора.
Вот такие нюансы случаются при ручном управлении памятью, тем более, когда он синхронный.