当基类和派生类都继承自boost::enable_shared_from_this时,出现了bad weak pointer。

7

我有一个基类,它派生自boost::enable_shared_from_this,还有另一个类,它同时派生自基类和boost::enable_shared_from_this:

#include <boost/enable_shared_from_this.hpp>
#include <boost/shared_ptr.hpp>

using namespace boost;

class A : public enable_shared_from_this<A> { };

class B : public A , public enable_shared_from_this<B> {
public:
    using enable_shared_from_this<B>::shared_from_this;
};

int main() {
shared_ptr<B> b = shared_ptr<B>(new B());
shared_ptr<B> b_ = b->shared_from_this();

return 0;
}

这段代码可以编译通过,但在运行时出现了错误

terminate called after throwing an instance of 'boost::exception_detail::clone_impl<boost::exception_detail::error_info_injector<boost::bad_weak_ptr> >'
  what():  tr1::bad_weak_ptr
Aborted

什么导致了这个问题,有没有什么解决办法?
编辑:
如果我需要像这样的东西:
class A : public enable_shared_from_this<A> { };
class B : public enable_shared_from_this<B> { };    

class C : public A, public B, public enable_shared_from_this<C> {
public:
    using enable_shared_from_this<C>::shared_from_this;
};

这意味着A和B都需要在自己身上拥有shared_from_this(它们不能从对方继承),而C需要A、B和shared_from_this。


我知道b_和b是一样的,在这种情况下我可以直接使用b。但这只是一个演示;在其他需要实际使用shared_from_this的情况下也会导致相同的错误。 - Ken
在这种情况下,我的解决方案将涉及虚拟继承。我怀疑boost可能已经有了一个类来处理这个问题,也许是enable_shared_from_this的非模板版本。但如果没有,我会创建自己的类,并在需要该功能的任何地方进行虚拟继承。当然,你必须使用dynamic_pointer_cast而不是static_pointer_cast。但它会起作用。 - Omnifarious
是的 - 我看不出明显的简洁方法使其工作。它接近于钻石继承,并且可能表明需要重新考虑设计。 - Fraser
2个回答

11

在给定的继承链中,不应该从enable_shared_from_this继承超过一次。

在这种情况下,可以让基类Aenable_shared_from_this继承,并使派生类B返回一个shared_ptr<A>,然后使用static_pointer_cast将其转换为shared_ptr<B>

或者,正如Omnifarious所指出的那样,您可以在B中编写一个函数来完成此操作。虽然与其重载shared_from_this(),我更倾向于使用显式命名的函数以最小化类的客户端出现意外情况的概率:

#include <boost/enable_shared_from_this.hpp>
#include <boost/shared_ptr.hpp>

using boost::shared_ptr;

class A : public boost::enable_shared_from_this<A> { };

class B : public A {
public:
    using enable_shared_from_this<A>::shared_from_this;
    shared_ptr<B> shared_B_from_this() {
        return boost::static_pointer_cast<B>(shared_from_this());
    }
    shared_ptr<B const> shared_B_from_this() const {
        return boost::static_pointer_cast<B const>(shared_from_this());
    }
};

int main() {
    shared_ptr<B> b = shared_ptr<B>(new B);
    shared_ptr<B> b1 = boost::static_pointer_cast<B>(b->shared_from_this());
    shared_ptr<B> b2 = b->shared_B_from_this();
    return 0;
}


5
以下是我解决你问题的方法:

以下是我解决你问题的方法:

#include <boost/enable_shared_from_this.hpp>
#include <boost/shared_ptr.hpp>

using namespace boost;

class virt_enable_shared_from_this :
   public enable_shared_from_this<virt_enable_shared_from_this>
{
 public:
   virtual ~virt_enable_shared_from_this() {}
};

template <class T>
class my_enable_shared_from_this : virtual public virt_enable_shared_from_this
{
 public:
   shared_ptr<T> shared_from_this() {
      return dynamic_pointer_cast<T>(virt_enable_shared_from_this::shared_from_this());
   }
};

class A : public my_enable_shared_from_this<A> { };

class B : public my_enable_shared_from_this<B> { };

class C : public A, public B, public my_enable_shared_from_this<C> {
 public:
   using my_enable_shared_from_this<C>::shared_from_this;
};

int main() {
   shared_ptr<C> c = shared_ptr<C>(new C());
   shared_ptr<C> c_ = c->shared_from_this();

   return 0;
}

这很痛苦,也有点难看。但是,它还算可以用。我认为重新考虑设计的想法更可能是更好的选择,就像Fraser所说。


如果没有从enable_shared_from_this继承virtual,我就不明白virt_enable_shared_from_this的目的是什么。 - Matthieu M.
@MatthieuM:enable_shared_from_this 是一个模板。因此,每个从模板继承的人都不能帮助解决问题,因为这些模板实例都是不同的。virt_enable_shared_from_this 是为每个人提供一个共同的基类来虚拟继承。 - Omnifarious
我认为你可以用static_pointer_cast替换dynamic_pointer_cast,这里是安全的...而且更快。 - kassak
@kassak:也许吧。我不想冒险。虚拟基类可能会以有趣的方式在类布局内浮动。 - Omnifarious

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接