Вызов 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, он называется:

  1. std::tr1::enable_shared_from_this<Derived>::shared_from_this
  2. std::tr1::shared_ptr<Derived>::shared_ptr<Derived> (this=0x7fffffffd370, __r=std::tr1::weak_ptr (empty) 0x2)
  3. std::tr1::__shared_ptr<Derived, (__gnu_cxx::_Lock_policy)2>::__shared_ptr<Derived>
  4. 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
Другие вопросы по тегам