shared_ptr
использует дополнительный объект-счетчик (он же «общий счет» или «блок управления») для хранения счетчика ссылок.
(Кстати: этот «счетчик» объект также хранит удалитель.)
Каждые shared_ptr
и weak_ptr
содержат указатель на фактический указатель и второй указатель на объект "counter".
Для реализации weak_ptr
объект "counter" хранит два разных счетчика:
- «Счетчик использования» - это число
shared_ptr
экземпляров, указывающих на объект.
- «Слабый счет» - это число
weak_ptr
экземпляров, указывающих на объект, плюс один, если «счетчик использования» все еще> 0.
Pointee удаляется, когда «счетчик использования» достигает нуля.
Вспомогательный объект-счетчик удаляется, когда «слабый счет» достигает нуля (это означает, что «счетчик использования» также должен быть равен нулю, см. Выше).
Когда вы пытаетесь получить shared_ptr
из weak_ptr
, библиотека атомарно проверяет «счетчик использования» и, если он> 0, увеличивает его. Если это удастся, вы получите shared_ptr
. Если «счетчик использования» уже был равен нулю, вы получаете пустой экземпляр shared_ptr
.
РЕДАКТИРОВАТЬ : Теперь, почему они добавляют один к слабому счету вместо того, чтобы просто отпустить объект «счетчик», когда оба счета падают до нуля? Хороший вопрос.
Альтернативой может быть удаление объекта «счетчик», когда и «счетчик использования», и «слабый счет» упадут до нуля. Вот первая причина: атомная проверка двух (размером с указатель) счетчиков невозможна на каждой платформе, и даже там, где она есть, это сложнее, чем проверка только одного счетчика.
Другая причина заключается в том, что средство удаления должно оставаться действительным, пока оно не завершит выполнение. Поскольку средство удаления хранится в объекте «counter», это означает, что объект «counter» должен оставаться действительным. Подумайте, что может произойти, если к какому-либо объекту есть один shared_ptr
и один weak_ptr
, и они одновременно сбрасываются в параллельных потоках. Допустим, shared_ptr
на первом месте. Он уменьшает «счетчик использования» до нуля и начинает выполнять удаление. Теперь weak_ptr
уменьшает «слабый счет» до нуля и находит, что «счетчик использования» также равен нулю. Таким образом, он удаляет объект «counter», а вместе с ним и «delete». Пока средство удаления еще работает.
Конечно, есть разные способы гарантировать, что «встречный» объект останется в живых, но я думаю, что увеличение «слабого счета» на единицу является очень элегантным и интуитивно понятным решением. «Слабый счет» становится счетчиком ссылок для объекта «счетчик». И поскольку shared_ptr
s также ссылаются на объект счетчика, они также должны увеличивать «слабый счет».
Вероятно, еще более интуитивным решением было бы увеличить «слабый счет» для каждого shared_ptr
, поскольку каждый отдельный shared_ptr
является ссылкой на объект «счетчик».
Добавление одного для всех shared_ptr
экземпляров - это просто оптимизация (при копировании / назначении shared_ptr
экземпляров сохраняется один атомарный прирост / уменьшение).