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

Помощник enable_shared_from_this содержит слабый указатель, который задается при создании общего указателя на объект. Это означает, что есть счетчик ссылок (выделенный отдельно или вместе с объектом с использованием make_shared) и дополнительный weak_ptr в объекте.

Теперь почему он просто не содержит счетчик ссылок? При установке shared_ptr из немого указателя тип должен быть полностью определен, поэтому конструктор или оператор shared_ptr может определить тип, полученный из enable_shared_from_this, и использовать правильный счетчик, и формат может оставаться неизменным, поэтому копирование не волнует. Фактически, shared_ptr уже должен обнаружить его для установки встроенного weak_ptr.

Ответ 1

Первое, что приходит на ум, это то, будет ли этот подход осуществимым вообще, и ответ заключается в том, что он не будет:

struct X : enable_shared_from_this {};
std::shared_ptr<X> p( new X );
std::weak_ptr<X> w( p );
p.reset();                      // this deletes the object
if ( w.use_count() ) {          // this needs access to the count object
                                //    but it is gone! Undefined Behavior

Если счетчик хранится в объекте, то weak_ptr может пережить объект, что является нарушением в контракте. Вся идея weak_ptr заключается в том, что они могут пережить объект (если последний shared_ptr выходит за пределы области видимости, объект удаляется, даже если есть weak_ptr)

Ответ 2

Разделение проблем: make_shared, чтобы вставить счетчик, enable_shared_from_this для shared_from_this.

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

Кроме того, Boost (откуда shared_ptr) также предлагает intrusive_ptr.

(подумайте, что ваше предложение, похоже, не позволяет создавать пользовательские удалители. Вы можете исправить это, изменив enable_shared_from_this на template<typename T, typename Deleter = default_deleter<T>> class enable_shared_from_this;, но к этому моменту он приближается к переосмыслению intrusive_ptr.)

Ответ 3

Чтобы получить какое-либо преимущество от встраивания счетчика в объект, вам понадобится убрать второй указатель в shared_ptr, который изменить его компоновку, а также создать проблемы для создания деструктора объект. Если вы измените макет, это изменение должно быть видимым везде используется shared_ptr. Это означает, что у вас не может быть экземпляр shared_ptr, указывающий на неполный тип.