Я немного поигрался с использованием 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 не должен знать, что он разрешает общий доступ из этого … (по крайней мере, если вы не спросите меня, или я что то снова пропускаю?)
Почему это, что я здесь скучаю?
Делать shared_from_this
Работа enable_shared_from_this
должен знать о shared_ptr
который держит класс. В вашей реализации STL это weak_ptr
, через другие реализации возможны. Когда вы наследуете конфиденциально, тогда невозможно получить доступ к свойствам базового класса извне вашего класса. На самом деле даже невозможно понять, что вы унаследовали от. Так make_shared
генерирует обычную инициализацию shared_ptr без установки соответствующих полей в enable_shared_from_this
,
Исключение выбрасывается не из make_shared
но форма shared_from_this
так как enable_shared_from_this
не был правильно инициализирован.
И нет ли способа заставить его работать для частного наследования, поскольку «внешний мир» класса shared_test не должен знать, что он разрешает общий доступ из этого …
Нет. Внешний мир должен знать, что объект имеет особые отношения с shared_ptr для правильной работы с ним.
нет ли способа заставить его работать для частного наследования, так как «внешний мир» класса shared_test не должен знать, что он разрешает общий доступ из этого
shared_ptr
сам является частью «внешнего мира»; shared_ptr
конструктор должен иметь возможность доступа к enable_shared_from_this
подобъект базового класса shared_test
объект, на который он указывает, для инициализации частного weak_ptr
член enable_shared_from_this
реализация.
Основываясь на документации, необходимо публично наследовать для доступности функции-члена «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, который разделяет владение * this
(функция публичного члена)
Я анализирую этот вопрос из кода в 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_black_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, может также быть установлен слабый_портал shared_test.