Почему общий ptr должен проводить подсчет ссылок для слабого ptr?

цитата из C++ Primer $12.1.6:

A weak_ptr (таблица 12.5) является интеллектуальным указателем, который не управляет временем жизни объект на который он указывает. Вместо этого weak_ptr указывает на объект, который управляется а shared_ptr. Создание и привязка к shared_ptr не меняет отсчет ссылок на это shared_ptr. Однажды последний shared_ptr указывая на объект исчезнет, сам объект будет удален. этот объект будет удален даже если есть weak_ptrs указываю на это-отсюда и название weak_ptr, который захватывает идея о том, что weak_ptr долей объекта "слабо."

тем не менее, я прочитал статьи говорит:

использование make_shared более эффективно. Реализация shared_ptr должна поддерживать домашнюю информацию в блоке управления, совместно используемом всеми shared_ptrs и weak_ptrs, ссылающимися на данный объект. В частности, что хозяйственная информация должна включать не только один, но и два отсчета:

  1. счетчик "сильная ссылка" для отслеживания количества shared_ptrs в настоящее время сохраняя объект живым. Общий объект уничтожается (и, возможно, освобождается), когда исчезает последняя сильная ссылка.

  2. подсчет "слабой ссылки" для отслеживания количества weak_ptrs, наблюдающих в настоящее время объект. блок управления общей уборкой уничтожается и освобождается (и общий объект освобождается, если он еще не был), когда исчезает последняя слабая ссылка.

насколько я знаю,shared_ptr создано make_shared в том же блок управления С этими счетами ref.Таким образом, объект не будет выпущен до последнего weak_ptr истекает.

вопросы:

  1. это неправильно праймер?, потому что weak_ptr воля фактически влияет на время существования этого объекта.
  2. почему shared_ptr нужно отслеживать его слабые ссылки?Weak_ptr может определить, существует ли объект, проверив сильный ссылок в блоках управления, поэтому я думаю, что блок управления не должен отслеживать слабые ссылки.
  3. просто для любопытства, что делает блок управления, созданный shared_ptr выглядеть?Это что-то вроде:

    template<typename T>
    class control_block
    {
       T object;
       size_t strong_refs;
       size_t weak_refs;
       void incre();
       void decre();
       //other member functions...
    };
    //And in shared_ptr:
    template<typename T>
    class shared_ptr
    {
       control_block<T> block;//Is it like this?So that the object and refs are in the same block?
       //member functions...
    };
    

3 ответов


счетчик ссылок управляет временем жизни объекта, на который указывает объект. Слабый граф - нет, но тут контроль (или участие в контроле) срок службы блок управления.

если счетчик ссылок идет в 0 объект уничтожил, но не обязательно освобожден. Когда слабый счет идет в 0 (или когда счетчик ссылок становится равным 0, если нет weak_ptrs, когда это случается), блок управления уничтожается и освобождается, а хранилище для объекта освобождается, если оно еще не было.

разделение между уничтожение и освобождение указанная на объект деталь реализации вам не нужно заботиться, но это вызвано использованием make_shared.

если у вас

shared_ptr<int> myPtr(new int{10});

вы выделяете хранилище для int, затем передайте это в shared_ptr конструктор, который выделяет хранилище для блока управления отдельно. В этом случае хранилище для int может быть освобожден как можно раньше: как только счетчик ссылок попадает 0, даже если есть еще слабая графа.

если у вас

auto myPtr = make_shared<int>(10);

затем make_shared может выполнить оптимизацию, где он выделяет хранилище для int и блок управления в одном дыхании. Это означает, что хранилище для int нельзя освободить до хранения для блок управления также может быть освобожден. Жизни int заканчивается, когда счетчик просмотров 0, но хранилище для него не освобождается до тех пор, пока слабый счетчик не попадет 0.

теперь понятно?


weak_ptr должен указывать на то, что может сказать, существует ли объект или нет, поэтому он знает, можно ли его преобразовать в shared_ptr. Поэтому для хранения этой информации необходим небольшой объект.

этот блок управления домашним хозяйством должен быть уничтожен при удалении последнего week_ptr (или shared_ptr). Поэтому он должен вести счет как shared_ptr, так и week_ptr.

обратите внимание, что блок управления уборкой не совпадает с объектом точка ПТР и поэтому week_ptr не влияют на жизнь объектов.

существует множество различных способов реализации интеллектуальных указателей в зависимости от того, какое поведение вы хотели бы иметь. Если вы хотите узнать больше, я бы рекомендовал "современный дизайн C++" Александреску (https://www.amazon.com/Modern-Design-Generic-Programming-Patterns/dp/0201704315)


и weak_ptr и shared_ptr указывают на память, содержащую блок управления. Если вы удалите блок управления, как только счетчик shared_ptr достигнет 0 (но слабый счетчик этого не делает), вы останетесь с weak_ptrs, указывающим на память мусора. Затем, когда вы пытаетесь использовать weak_ptr, он считывает освобожденную память и происходят плохие вещи (UB).

по этой причине блок управления должен быть оставлен в живых (выделен и построен, не уничтожен и не освобожден), пока любой weak_ptr может попытаться прочитать он.

основной (заостренный) объект будет уничтожен и может (надеюсь) быть освобожден, как только общий счетчик достигнет 0. Блок управления будет уничтожен и освобожден, когда оба счетчика достигнут 0.