Умные указатели, чтобы правильно выполнять свою работу, поддерживают так называемый блок управления , который служит хранилищем метаданных, в частности, используют счетчики. То есть каждый ресурс имеет связанный блок управления в памяти (состоящий, например, из двух целых чисел), на который могут ссылаться интеллектуальные указатели, чтобы узнать, сколько из них все еще используют / наблюдают за ресурсом. Очевидно, что каждый существующий std::shared_ptr
увеличивает счетчик использования, хранящийся в блоке управления, так что его деструктор знает, пора ли освобождать ресурс при уничтожении. std::weak_ptr
, в свою очередь, отслеживает только объект и его блок управления. Обратите внимание на важную деталь: std::weak_ptr
не увеличивает счетчик использования. Это желательно, так как его основная цель - разорвать возможные циклы между парой объектов, наблюдающих друг за другом. То есть, если два объекта будут хранить std::shared_ptr
один в другом, то такая пара объектов также будет поддерживать друг друга бесконечно.
Как может std::weak_ptr
узнать, может ли ресурс быть lock()
ed? Это может быть успешным, только если счетчик использования больше нуля. Он знает это из блока управления (который сам остается живым в памяти до тех пор, пока за ним наблюдают ненулевые слабые указатели).
В первом примере:
std::weak_ptr<int> weakPtr1 = std::make_shared<int>(6);
как ресурс (int=6
) выделяется, а также его блок управления. Счетчик использования становится 1
и остается таким, пока жив std::shared_ptr
. Затем инициализируется std::weak_ptr
, получая указатель на блок управления. Здесь он не увеличивает счетчик использования. Однако это увеличит счетчик слабых указателей. На данный момент оба счетчика имеют 1
. Затем, ставя точку с запятой ;
, временный std::shared_ptr
удаляется. Это уменьшает счетчик использования до 0
. Это означает, что больше нет общих указателей, разделяющих владение ресурсом, что позволяет освободить этот ресурс. Тем не менее, существует 1
слабый указатель, наблюдающий за блоком управления, что означает, что сам блок управления останется в памяти, так что weakPtr1
знает, что lock()
он больше не сможет *1026* ресурс (потому что этот ресурс больше не существует).
Во втором примере:
std::shared_ptr<int> sharedPtr = std::make_shared<int>(99);
std::weak_ptr<int> weakPtr2 = sharedPtr;
как ресурс int=99
, так и его блок управления остаются активными. Следовательно, weakPtr2
может быть заблокирован до тех пор, пока sharedPtr
(или любая из его копий) не будет уничтожена.