Многие ответы касаются способа хранения счетчика ссылок (он хранится в общей памяти для всех shared_ptr, которые содержат тот же собственный указатель), но большинство исключает проблему утечек.
Самый простой способ утечки памяти с помощью указателей с подсчетом ссылок - создание циклов. Например, гарантированно не будет удален двусвязный список, в котором все указатели являются shared_ptr, по крайней мере, с двумя элементами. Даже если внешние указатели будут освобождены, внутренние указатели все равно будут учитываться, а счетчик ссылок не достигнет 0. Это, по крайней мере, в самой наивной реализации.
Самым простым решением проблемы цикла является смешивание shared_ptr (указатели с подсчетом ссылок) со слабыми указателями, которые не разделяют владение объектом.
Общие указатели будут совместно использовать ресурс (указатель) и дополнительную информацию reference_count. Когда вы используете слабые указатели, счетчик ссылок удваивается: есть счетчик ссылок общего указателя и счетчик ссылок слабого указателя. Ресурс освобождается всякий раз, когда число общих указателей достигает 0, но информация reference_count остается активной до тех пор, пока не будет освобожден последний слабый указатель.
В двусвязном списке внешняя ссылка хранится в shared_ptr, тогда как внутренние ссылки просто слабые_ptr. Всякий раз, когда нет внешних ссылок (shared_ptr), элементы списка освобождаются, удаляя слабые ссылки. В конце все слабые ссылки были удалены, а последний слабый указатель на каждый ресурс освобождает информацию reference_count.
Это менее запутанно, чем кажется вышеупомянутым текстом ... Я попробую позже.