СТД:: enable_shared_from_this; общественный против частного
Я немного поиграл с использованием shared_ptr и enable_shared_from_this, в то время как я сталкивался с тем, чего я действительно не понимаю.
В моей первой попытке я построил что-то вроде этого:
class shared_test : std::enable_shared_from_this<shared_test> {
public:
void print(bool recursive) {
if (recursive) {
shared_from_this()->print(false);
}
std::cout << "printing" << std::endl;
}
};
Обратите внимание, что этот класс расширяет std:: enable_shared_from_this в частном порядке. Это, по-видимому, имеет большое значение, поскольку выполняется что-то вроде этого:
int main() {
auto t(std::make_shared<shared_test>());
t->print(true);
return 0;
}
генерирует исключение bad_weak_ptr. Где, как если бы я изменил определение класса, чтобы оно было открыто публично из std:: enable_shared_from_this, этот пробег просто находит.
Почему, что мне здесь не хватает? И нет ли способа заставить его работать для частного наследования, поскольку "внешний мир" класса shared_test не должен знать, что он разрешает совместное использование этого... (по крайней мере, если вы не спросите меня, или я что-то пропущу?)
Ответы
Ответ 1
Почему, что мне здесь не хватает?
Чтобы сделать shared_from_this
работа enable_shared_from_this
должна знать о shared_ptr
, которая содержит класс. В вашей реализации STL это weak_ptr
, возможны другие реализации. Когда вы наследуете конфиденциально, тогда невозможно получить доступ к свойствам базового класса извне вашего класса. На самом деле даже невозможно понять, что вы унаследовали. Таким образом, make_shared
генерирует обычную инициализацию shared_ptr без установки правильных полей в enable_shared_from_this
.
Исключение выбрано не из make_shared
, а form shared_from_this
, потому что enable_shared_from_this
не был правильно инициализирован.
И нет ли способа заставить его работать для частного наследования, поскольку "внешний мир" класса shared_test не должен знать, что он разрешает совместное использование этого...
Нет. Внешний мир должен знать, что объект имеет особые отношения с shared_ptr для правильной работы с ним.
Ответ 2
не существует способа заставить его работать для частного наследования, так как "внешний мир" класса shared_test не должен знать, что он разрешает совместное использование этого
shared_ptr
сам является частью "внешнего мира"; конструктор shared_ptr
должен иметь возможность доступа к подобъекту базового класса enable_shared_from_this
объекта shared_test
, на который он указывает, для инициализации частного weak_ptr
члена реализации enable_shared_from_this
.
Ответ 3
Основываясь на документации, необходимо публично наследовать доступ к функции членства "shared_from_this".
"Публично наследование из std:: enable_shared_from_this предоставляет тип T с функцией-членом shared_from_this" - из ссылки CPP
http://en.cppreference.com/w/cpp/memory/enable_shared_from_this
shared_from_this:
возвращает shared_ptr, который имеет право владения * этим
(функция открытого участника)
Ответ 4
Я анализирую этот вопрос из кода в STL:
auto t (std :: make_shared());
строка кода создает shared_ptr; сначала мы погрузимся в функцию make_shared
// FUNCTION TEMPLATE make_shared
template<class _Ty,
class... _Types>
NODISCARD inline shared_ptr<_Ty> make_shared(_Types&&... _Args)
{ // make a shared_ptr
const auto _Rx = new _Ref_count_obj<_Ty>(_STD forward<_Types>(_Args)...);
shared_ptr<_Ty> _Ret;
_Ret._Set_ptr_rep_and_enable_shared(_Rx->_Getptr(), _Rx);
return (_Ret);
}
Внимание: мы погрузимся в функцию _Ret.Set_ptr_rep_and_enable_shared.И мы можем видеть следующее:
template<class _Ux>
void _Set_ptr_rep_and_enable_shared(_Ux * _Px, _Ref_count_base * _Rx)
{ // take ownership of _Px
this->_Set_ptr_rep(_Px, _Rx);
_Enable_shared_from_this(*this, _Px);
}
Итак, мы находим функцию _Enable_shared_from_this, далее:
template<class _Other,
class _Yty>
void _Enable_shared_from_this(const shared_ptr<_Other>& _This, _Yty * _Ptr)
{ // possibly enable shared_from_this
_Enable_shared_from_this1(_This, _Ptr, _Conjunction_t<
negation<is_array<_Other>>,
negation<is_volatile<_Yty>>,
_Can_enable_shared<_Yty>>{});
}
Мы находим ключевую точку: _Can_enable_shared <_Yty>
template<class _Yty,
class = void>
struct _Can_enable_shared
: false_type
{ // detect unambiguous and accessible inheritance from enable_shared_from_this
};
template<class _Yty>
struct _Can_enable_shared<_Yty, void_t<typename _Yty::_Esft_type>>
: is_convertible<remove_cv_t<_Yty> *, typename _Yty::_Esft_type *>::type
{ // is_convertible is necessary to verify unambiguous inheritance
};
мы находим, что только _Yty имеет _Esft_type, а _Yty может быть преобразовано в _Esft_type, может _Yty может быть enable_shared (Если вы хотите узнать больше, то, чтобы увидеть set weak_ptr в _Yty, или вы можете получить ошибку bad_weak_ptr при использовании shared_from_this). Так что же такое _Esft_type?
template<class _Ty>
class enable_shared_from_this
{ // provide member functions that create shared_ptr to this
public:
using _Esft_type = enable_shared_from_this;
...
}
поэтому _Esft_type означает просто enable_shared_from_this <_Ty>, поэтому, если вы используете приватное наследование, извне не только не будет видно _Esft_type, а _Yt не может быть преобразовано в _Esft_type. Таким образом, weak_ptr не может быть установлен, так что bad_weak_ptr может быть вызван.
Таким образом, извне нужно знать о существовании _Esft_type, поэтому, когда создается shared_ptr, может также быть установлен weak_ptr для shared_test.