Почему «weak.lock ()» возвращает «nullptr» с определением «auto weak = std :: make_shared <int>(42);»? - PullRequest
5 голосов
/ 16 июня 2020

Почему weak.lock() возвращает nullptr в этом фрагменте кода:

   std::weak_ptr<int> weakPtr1 = std::make_shared<int>(6);
   std::cout << weakPtr1.lock() << std::endl;

, тогда как он работает в следующем:

   std::shared_ptr<int> sharedPtr = std::make_shared<int>(99);
   std::weak_ptr<int> weakPtr2 = sharedPtr;
   std::cout << weakPtr2.lock() << std::endl;

Check cpp .sh / 9gkys.

Я думал и думал об этом, но я все еще запутался. Буду признателен за помощь с этим вопросом.

Ответы [ 2 ]

4 голосов
/ 16 июня 2020

Умные указатели, чтобы правильно выполнять свою работу, поддерживают так называемый блок управления , который служит хранилищем метаданных, в частности, используют счетчики. То есть каждый ресурс имеет связанный блок управления в памяти (состоящий, например, из двух целых чисел), на который могут ссылаться интеллектуальные указатели, чтобы узнать, сколько из них все еще используют / наблюдают за ресурсом. Очевидно, что каждый существующий 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 (или любая из его копий) не будет уничтожена.

3 голосов
/ 16 июня 2020

В ваших примерах используется инициализация копии. Таким образом, shared_ptr, построенный справа, живет только до вычисления выражения, а затем уничтожается:

  1. В первом примере это означает, что больше нет ссылок на shared_ptr (мы не считайте слабый), и, следовательно, lock возвращает null.
  2. Во втором вы привязываете результат к локальной переменной, продлевая время жизни текущего блока - и, следовательно, все еще есть ссылка , и нет нулевого результата.
...