Существует цикл доступности объектов через указатели реализации, то есть вы можете следовать указателям, используемым внутри частных реализаций этих объектов, и возвращаться к тому, с чего вы начали: a->parent
содержит указатель на метаинформацию (блок управления), созданный либо std::shared_ptr
, либо std::make_shared
.
Конечно, ожидается, что std::make_shared
сведет к минимуму количество динамических распределений памяти путем объединения метаинформации и управляемого объекта, но это не имеет никакого отношения к тому, когда управляемый объект будет уничтожен (что является единственным наблюдаемым аспектом, поскольку нет специфичных для класса operator new
/operator delete
). был использован). Таким образом, не имеет значения, находится ли управляющий блок вместе с управляемым объектом или имеет указатель на этот объект, выделенный отдельно.
Во всех, кроме нескольких вырожденных случаях (где интеллектуальный указатель создается с помощью фальшивого удаления, которое ничего не освобождает), окончание срока службы последнего общего интеллектуального указателя-владельца приводит к запуску удаления, как правило:
- формы
delete p;
для запуска, где p
— аргумент типа T*
конструктора std::shared_ptr<U>
,
- или формы
p->~T()
, где p
является результатом new (buff) T
в std::make_shared<T>()
.
В любом случае значение p
можно получить из метаинформации.
[Обратите внимание, что удаляемое значение p
никогда не получается из значения указателя U*
, хранящегося в каком-либо конкретном экземпляре std::shared_ptr<U>
, поскольку такое значение указателя не может быть "удаляемым", поскольку деструктор не может быть виртуальным (пока аргумент указателя std::shared_ptr<U>
имеет правильный статический тип: достаточно, чтобы delete p;
, где p
— значение типа, переданного шаблонному конструктору), поскольку указатель может относиться к подобъекту-члену или полному другому завершенному объекту, если конструктор псевдонимов использовался для создания другого std::shared_ptr
с общей собственностью.]
Таким образом, наличие указателя на управляющий блок обычно позволяет восстановить указатель на контролируемый объект, хотя этот указатель на весь объект нельзя получить через общедоступный интерфейс (за исключением того, что указатель передается удаляющему, поэтому единственный способ восстановить указатель в С++, если он был утерян, нужно было передать пользовательский модуль удаления и дождаться его вызова). Указатель, безусловно, может быть восстановлен путем навигации внутри представления памяти (хотя для этой навигации может потребоваться использовать dynamic_cast
для типа, неизвестного во время компиляции, что-то, что отладчик сможет сделать, если он знает обо всех производных классах).
Итак, у нас есть цикл:
a
a->parent
parent->control_block
control_block.deleter (virtual call or stored function)
deleter.a
если указатель хранится в динамически созданном средстве удаления, что необходимо для создания std::shared_ptr<U>(T*)
, или
a
a->parent
parent->control_block
control_block.buffer
для объектов, созданных с помощью одного выделения make_shared
: объект был создан внутри этого буфера, поэтому &control_block.buffer == a
.
Но циклы указателей - это не проблема, а только цикл владения, поскольку он подразумевает «владение собой, контролируемое временем жизни», то есть «я уничтожу себя только тогда, когда моя жизнь закончится» (иначе «я войду в деструктор, когда у меня будет введен деструктор"), абсурд.
Здесь нет собственности, поскольку слабая ссылка владеет только метаинформацией, а не самой информацией.
person
curiousguy
schedule
13.06.2018