从基类派生中调用shared_from_this()会给出std :: bad_weak_ptr

时间:2014-05-19 09:37:30

标签: c++ inheritance shared-ptr tr1

我有一个Base类和一个Derived类。 API仅公开Derived,并且实现隐藏在Base中(gcc属性visibility设置为hidden),因为一些内部API模块必须调用Base方法。 这样我们得到了:

// 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
  5. __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     }
    

    我的__r._M_pi等于0。

    我知道在Derived中使用Base可能是一个糟糕的主意,但这不是主题的问题,让我们按原样来看待它。 (没有人可以直接实例化Base

    客户(使用API​​的人)使用shared_ptr的重要性,这就是Derived继承enable_shared_from_this的原因。

    我正在尝试做的是了解发生了什么以及为什么抛出bad_weak_ptr异常以及我该怎么做才能避免它(可能是架构中的一些小变化)。


    修改

    我根据https://stackoverflow.com/a/9377075/1498245

    更改了Base::doSomething

    现在看起来像:

    void Base::doSomething(DerivedPtr aDerived)
    {
        DerivedPtr lDerived = std::tr1::static_pointer_cast<Derived>( static_cast<Derived*>(this)->shared_from_this() );
    }
    

    可能开始起作用了。可能,因为不再有例外,但我不太确定下面发生了什么。在reinterpret_cast期间看起来有一些数据丢失。在我的实际案例中,Base类更大,有很多成员,等等。也许就是这种情况。谁能对这个案子有所启发?发生了什么“引擎盖下”?

1 个答案:

答案 0 :(得分: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