无法替代shared_from_this()

时间:2014-06-19 15:09:17

标签: c++ c++11

有时课程需要获得shared_ptr。实现此目的的一种方法(当您知道该类的对象将始终存在于shared_ptr s中)时,可以从std::enable_shared_from_this<SomeClass>派生并根据需要调用shared_from_this()

但是这种方法的一个小问题是你无法从析构函数中调用shared_from_this() - 因为enable_shared_from_this在引擎盖下使用weak_ptr并且因为在析构函数中意味着对象的引用计数为0,将抛出bad_weak_ptr异常。

现在,我经常发现自己正在编写具有非平凡析构函数的类,这些析构函数会调用其他尝试使用shared_from_this()的函数。例如:

~SomeClass::SomeClass()
{
   f();
}

// f can also be called outside the context of the destructor
/* public */ void SomeClass::f()
{
   g();
   m_anotherObject.h(shared_from_this()); // want to skip this if shared_from_this() fails
   i();
}

如前所述,写入的f将在从析构函数调用时抛出bad_weak_ptr异常。我可以简单地吞下该异常并转到i(),但这违反了只在特殊情况下抛出异常的想法,并且当IDE配置为在抛出异常时向您发出警告时,调试会变得更加困难。我还可以用任何数量的不同方法重构代码,以防止从析构函数中调用h,但这会增加复杂性,损害可读性,并且似乎有很多不必要的麻烦。或者我可以添加某种bool m_inDestructor成员变量,但这将是可怕的。

/* public */ void SomeClass::f()
{
   g();

   try
   {   
      m_anotherObject.h(shared_from_this());
   }
   catch(std::bad_weak_ptr&) {} // don't like; exceptions should be exceptional

   i();
}

/////////////////////

SomeClass::~SomeClass()
{
   f(true);
}

/* public */ void SomeClass::f()
{
   f(false);
}

// works, but adds complexity I feel like I shouldn't need
/* private */ void SomeClass::f(bool inDestructor)
{
   g();
   if(!inDestructor)
      m_anotherObject.h(shared_from_this());
   i();
}

所以我的问题是:是否有人提出了一般适用的非投掷机制来确定enable_shared_from_this是否实际上有shared_ptr提供?

2 个答案:

答案 0 :(得分:2)

我正在回答这个问题,因为我不认为我可以在评论中解释清楚。

这通常是您过度使用共享指针的好兆头!当您需要管理对象的生命周期时,您应该只使用共享指针。创建共享指针的实例意味着您说“我需要对此对象拥有一些所有权”。这非常适合交错数据结构。

你不应该在函数调用中使用共享指针(通常)。函数调用通常是输入输出。函数调用的参数的生命周期必须通过函数调用持续,但调用者通常负责确保函数调用,而不是函数。

请考虑以下代码:

class SomeClass;//implements enable_shared_from_this
int foo(shared_ptr<SomeClass> a);
int bar(shared_ptr<SomeClass> a);

int main()
{
   SomeClass c;
   foo(c.shared_from_this());
   bar(c.shared_from_this());
}

我们知道整个电话中都存在c:它不能超出范围。要求共享指针输入限制谁可以使用该类并增加调用的开销,并且不会给我们提供比我们已有的更多保护。更糟糕的是它增加了一个错误条件(shared_ptrs仍然可以为空!)。

替代方案更快,更灵活:

class SomeClass;//implements enable_shared_from_this
int foo(SomeClass& a);
int bar(SomeClass& a);

int main()
{
   SomeClass c;
   foo(c);
   bar(c);
}

通过引用,我们已经确定实例在传入时必须存在。并且让调用者确保引用持续足够长的时间。如果c可以处于销毁/默认状态,那么foo和bar无论如何都需要检测到它。

根据您的具体示例,我们应该致电

 int SomeClass::foo()
 {
    foo(*this);
 }

this必须存在,因为我们正在调用该函数。如果SomeClass可以拥有你想要处理的被破坏状态,那么你应该有一个可以在通话中检查的方法(可以是私有的或公共的)bool is_destroyed() const(虽然老实说我会让调用者负责好吧,只是说“你试图使用被破坏的物体?你是SOL”。

编辑1:使用shared_ptr的例外情况是,如果该函数的副作用是获取该类的所有权(如启动线程,设置全局或返回具有所有权的类),但是如果您实现enabel_shared_from_this我仍然会参考。

编辑2:SharedFromThis是一个拐杖(imho),因为以下内容会创建一个错误状态:

 shared_ptr<SomeClass> ptr;
 {
    SomeClass a;
    ptr = a.shared_from_this();
 }
 ptr->foo();//because the parent went out of scope, it is destroyed anyway!

这就是调用者应该只管理自己的共享指针的原因。

答案 1 :(得分:1)

如果你真的想这样做,你可以自己重新实现shared_from_this成语,除非它在适当时返回null shared_ptr

要执行此操作,请将weak_ptr<SomeClass>添加到SomeClass作为数据成员:

class SomeClass
{
    std::weak_ptr<SomeClass> weak_ptr_;

在工厂功能中,初始化weak_ptr

static std::shared_ptr<SomeClass> create()
{
    std::shared_ptr<SomeClass> p(new SomeClass);
    p->weak_ptr_ = p;
    return p;
}

然后写shared_from_this来做你想做的事:

std::shared_ptr<SomeClass> shared_from_this() {return weak_ptr_.lock();}

现在,只要f()可以处理空shared_ptr(并且只要您通过工厂功能控制所有构造),您就会变得金黄。