Как удаляется контрольный блок у std::shared_ptr()?
Есть момент который я не очень понимаю.
Если я не ошибаюсь то, при работе с std::weak_ptr() имеет значение, как мы создали std::shared_ptr().
auto sp1 = std::shared_ptr<int>(new int(10)); // 1
auto sp2 = std::make_shared<int>(10); // 2
Разница между 1 и 2 в выделение памяти для объекта и контрольного блока(раздельно или вместе). Когда мы работаем с std::weak_ptr() мы можем проверить его на "просроченность" и даже если счетчик сильных ссылок = 0, то пока есть сам std::weak_ptr() будет существовать контрольный блок и вот я наконец подошел к вопросу!
Когда мы выделили память через std::make_shared<>(), затем у нас остался только сам weak_ptr получается у нас есть контрольный блок, но т.к. мы выделили память вместе то и объект вроде как бы есть, так что творится с этим объектом или выделенной памятью?
Ответы (2 шт):
Контрольный блок в действительности - это возможная реализация поведения.
Все , что гарантируется - это при копировании shared_ptr указателя use_count() "откуда-то" берет значение счетчика использования увеличенное на единицу используя атомарное чтение (скорее всего с memory_order_relaxed). Нужно еще не забывать, что shared_ptr на вход получает еще и манипулятор удаления, который может быть вызван конструкторе, если в конструкторе произошло исключение. При удалении указателя shared_ptr уменьшает это значение на единицу и удаляет указатель с помощью заданного манипулятора удаления.
Т.е. объект УЖЕ может существовать при нулевом счетчика и ЕЩЕ может существовать в момент его обнуления. weak_ptr считывает значение того же счетчика, и если оно нулевое, считает, что указатель эквивалентен nullptr. Таким образом гарантируется достаточная корректность значения weak_ptr.
Контрольный блок - это некая структура, связывающая значение счетчика с указателем. Счетчик уже должен быть корректным во время создания указываемого объекта при конструировании shared_ptr и ЕЩЕ обязан существовать при удалении последней копии weak_ptr, и это все, что нас должно волновать.
Деструктор объекта в любом случае зовется когда умирает последний shared_ptr, но это не обязательно совпадает с освобождением его памяти.
Если использовали make_shared, то память под объект (+ контрольный блок) остается выделенной, пока не удалят контрольный блок.