А shared_ptr
3 вещи. Это контрольный счетчик, разрушитель и принадлежащий ему ресурс.
Когда вы make_shared
, он выделяет все 3 сразу, а затем создает их в одном блоке.
Когда вы создаете shared_ptr<T>
с T*
, вы создаете контрольный счетчик/эсминца отдельно и обратите внимание, что принадлежащий ему ресурс - это T*
.
Цель shared_from_this
состоит в том, что мы можем извлечь shared_ptr<T>
из T*
в основном (в предположении, что он существует).
Если все общие указатели, созданные с помощью make_shared
, это будет легко (если вы не хотите, чтобы определенное поведение было при сбое), так как компоновка проста.
Однако не все общие указатели создаются именно так. Иногда вы можете создать общий указатель на объект, который не был создан никакой функцией библиотеки std
, и, следовательно, T*
не связан с данными подсчета и уничтожения ссылок общего указателя.
Как нет номер в T*
или что он указывает (в целом), чтобы найти такие конструкции, мы должны хранить его снаружи, что означает глобальное состояние и потокобезопасности накладные расходы и другие боли.Это будет обузой для людей, которые не нуждаются в shared_from_this
, и производительность по сравнению с текущим состоянием для людей, которые в ней нуждаются (мьютексы, поиск и т. Д.).
Настоящий проект хранит weak_ptr<T>
в enable_shared_from_this<T>
. Этот weak_ptr
инициализируется всякий раз, когда вызывается make_shared
или shared_ptr<T>
ctor. Теперь мы можем создать shared_ptr<T>
из T*
, потому что у нас есть «сделанная комната» для него в классе, наследуя от enable_shared_from_this<T>
.
Это очень низкая стоимость и очень хорошо обрабатывает простые случаи. Мы закончили с накладными расходами одного weak_ptr<T>
по базовой стоимости T
.
Если у вас есть два различных shared_from_this
, их weak_ptr<A>
и weak_ptr<B>
члены не имеют никакого отношения, поэтому он является неоднозначным, где вы хотите сохранить получившийся смарт-указатель (возможно, и другое?). Эта двусмысленность приводит к ошибке, которую вы видите, так как предполагает, что в одном уникальном shared_from_this<?>
есть один член weak_ptr<?>
, и на самом деле есть два.
The linked solution обеспечивает умный способ продлить это. Он пишет enable_shared_from_this_virtual<T>
.
Здесь вместо хранения weak_ptr<T>
, мы храним weak_ptr<Q>
где Q
является виртуальным базовым классом enable_shared_from_this_virtual<T>
, и делает это уникально в виртуальном базовом классе. Затем он практически не переопределяет shared_from_this
и аналогичные методы обеспечивают тот же интерфейс, что и shared_from_this<T>
, используя «указатель-член или дочерний тип конструктора shared_ptr
», в котором вы разбиваете компонент счетчика/уничтожителя ссылок из принадлежащего ему ресурсного компонента в безопасном типе путь.
над головой здесь больше, чем основной shared_from_this
: он имеет виртуальное наследование и заставляет виртуальный деструктор, который означает, что объект хранит указатель на таблицу виртуальных функций, а также доступ к shared_from_this
медленнее, поскольку это требует таблицу виртуальных функций отправка.
Преимущество «просто работает». В иерархии теперь есть один уникальный shared_from_this<?>
, и вы все равно можете получить общие указатели типа к классам T
, которые наследуются от shared_from_this<T>
.
Вы имеете в виду это как замену/эквивалент C++ 11, для другого вопроса, связанного с форсированием? –
C++ 11. Есть ли что-то другое в версии C++ 11, о которой я должен знать? – Taylor
Не уверен. Но, как и все, мы просто немного сузились для меня по сравнению с связанными вопросами и ответами (ваш вопрос и ответ). Вы должны попытаться немного улучшить это. –