Вызов shared_from_this() из производного в базовом классе дает std::bad_weak_ptr
У меня есть Базовый класс и Производный класс. API предоставляет только Derived, а реализация скрыта в Base (с атрибутом gcc visibility
установлен в hidden
), так как некоторые из внутренних модулей API должны вызывать базовые методы. Таким образом, мы получили:
// Base.h
class Derived;
class Base
{
typedef std::tr1::shared_ptr<Derived> DerivedPtr;
public:
void doSomething(DerivedPtr aDerived);
protected:
Base();
};
// Derived.h
class Derived : public std::tr1::enable_shared_from_this<Derived>, public Base
{
public:
Derived():std::tr1::enable_shared_from_this<Derived>(), Base(){}
void doSomething(DerivedPtr aDerived)
{
Base::doSomething(aDerived);
}
};
Что теперь doSomething
делает это следующим образом:
void Base::doSomething(DerivedPtr aDerived)
{
DerivedPtr lDerived = reinterpret_cast<Derived*>(this)->shared_from_this();
}
Проблема заключается в следующем:
- когда я использую простой пример, как показано выше - он работает нормально;
- когда я использую его в моем большом проекте - он бросает
std::tr1::bad_weak_ptr
исключение;
Я использую gcc-4.4.7, и из того, что я могу видеть из backtrace, он называется:
std::tr1::enable_shared_from_this<Derived>::shared_from_this
std::tr1::shared_ptr<Derived>::shared_ptr<Derived> (this=0x7fffffffd370, __r=std::tr1::weak_ptr (empty) 0x2)
std::tr1::__shared_ptr<Derived, (__gnu_cxx::_Lock_policy)2>::__shared_ptr<Derived>
std::tr1::__shared_count<(__gnu_cxx::_Lock_policy)2>::__shared_count
В __shared_count
это бросает, потому что:
292 template<_Lock_policy _Lp>
293 inline
294 __shared_count<_Lp>::
295 __shared_count(const __weak_count<_Lp>& __r)
296 : _M_pi(__r._M_pi)
297 {
298 if (_M_pi != 0)
299 _M_pi->_M_add_ref_lock();
300 else
301 __throw_bad_weak_ptr();
302 }
my __r._M_pi равен 0.
Я знаю, что это может быть ужасная идея, чтобы использовать Derived
в Base
, но это не вопрос предмета, давайте просто примем это как есть. (Никто не может создать экземпляр Base
напрямую).
Что важно, клиент (который использует API) использует shared_ptr, поэтому Derived наследует enable_shared_from_this
,
И я пытаюсь понять, что происходит и почему bad_weak_ptr
выдается исключение и что я могу сделать, чтобы избежать его (возможно, некоторые незначительные изменения в архитектуре).
РЕДАКТИРОВАТЬ
Я изменился Base::doSomething
в соответствии с: /questions/39443852/badweakptr-pri-vyizove-sharedfromthis-v-bazovom-klasse/39443857#39443857
Теперь это выглядит так:
void Base::doSomething(DerivedPtr aDerived)
{
DerivedPtr lDerived = std::tr1::static_pointer_cast<Derived>( static_cast<Derived*>(this)->shared_from_this() );
}
и это, вероятно, начало работать. Возможно, потому что больше нет исключений, но я не совсем уверен, что происходит под ним. Похоже, во время reinterpret_cast
произошла некоторая потеря данных. В моем случае реального мира Base
класс намного больше, имеет много членов и т. д. Может быть, это так. Кто-нибудь может пролить свет на этот случай? Что происходит "под капотом"?
1 ответ
Проблема заключается в следующем:
• когда я использую простой пример, как показано выше - он работает нормально;
• когда я использую его в своем большом проекте - он вызывает исключение std::tr1::bad_weak_ptr;
Это звучит для меня как неопределенное поведение.
На ум приходят две возможности (так как вы не опубликовали различий между тестовым кодом и вашим проектом, я думаю, здесь):
Моим первым предположением будет такой код:
DerivedPtr lDerived = reinterpret_cast<Derived*>(this)->shared_from_this();
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Вы уверены, что звоните Base::doSomething
только из производных экземпляров? Это небезопасное приведение к основанию иерархии.
Мое второе предположение заключается в том, что вы вызываете shared_from_this для чего-то, что не является указателем. Пример сценария, о котором я говорю:
struct Foo : public std::enable_shared_from_this<Foo> {
std::shared_ptr<Foo> share() { return shared_from_this(); }
};
std::shared_ptr<Foo> pf(new Foo);
auto pf2 = pf->share(); // OK, pf2 will share ownership with pf
Foo localInstance;
auto pf2 = localInstance.share(); // NOT OK, pf2 will attempt to delete a local
// value at scope end