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