【问题标题】:questions regarding shared_from_this关于 shared_from_this 的问题
【发布时间】:2011-03-08 12:52:07
【问题描述】:

我有一个接受shared_ptr<MyClass> 的函数。 在MyClass 的某些成员函数memfun 中,我需要将this 传递给该函数。但是如果我写

void MyClass:memfun()
{
   func(shared_ptr<MyClass>(this))
}

我假设调用结束后引用计数将达到 0,并且将尝试销毁 this,这很糟糕。

然后我记得有这个类 enable_shared_from_this 和函数 shared_from_this

所以现在我将使用以下内容:

class MyClass: public enable_shared_from_this<MyClass>
{
    void MyClass:memfun()
    {
       func(shared_from_this());
    }
};

问题是:

1) 如果不从 enable_shared_from_this 派生,绝对不可能使用该功能吗?
2)enable_shared_from_this 派生是否意味着调用 memfun具有自动存储期限的对象会导致不好的事情吗?例如

 int main()
 { 
    MyClass m;   //is this OK?
    m.memfun();  // what about this?
 }

3) 如果我从 MyClass 派生,enable_shared_from_this 功能会被正确继承还是需要再次派生?也就是说,

class MyCoolClass: public Myclass
{
   void someCoolMember
   {
      someCoolFuncTakingSharedPtrToMyCoolClass(shared_from_this());
   }
}

这样好吗?或者正确的是以下?

 class MyCoolClass: public Myclass, public enable_shared_from_this<MyCoolClass>
    {
       void someCoolMember
       {
          someCoolFuncTakingSharedPtrToMyCoolClass(enable_shared_from_this<MyCoolClass>::shared_from_this());
       }
    }   

非常感谢。

【问题讨论】:

    标签: c++ shared-ptr


    【解决方案1】:

    1)这取决于你所说的“做这个”是什么意思,你是否可以。您始终可以从诸如this 的原始指针构造shared_ptr,但它不会与另一个从原始指针单独构造的shared_ptr 实例共享引用计数。因此,您将需要在一个或其他实例上使用自定义删除器以避免双重删除,但除非您非常小心,否则您可能最终会因为对象被删除而悬空shared_ptr 实例,但仍可从另一个实例访问.

    shared_from_this 使您能够保证,如果您的对象有一个 shared_ptr 实例,那么您可以在不复制第一个的情况下构造另一个实例,并且这些实例将共享引用计数。您可以通过将 weak_ptr 存储为类成员并在您首次为对象分配 shared_ptr 时设置该值来实现此目的。

    2) 调用shared_from_this() 要求至少有一个shared_ptr 实例已经指向您的对象。如果您在没有带有自定义删除器的shared_ptr 实例的自动对象上使用它,那么您将遇到不好的事情。

    3) 如果您从您的类派生,那么enable_shared_from_this 功能将为您提供基类(派生自enable_shared_from_this)的shared_ptr。然后,您可以使用static_pointer_castdynamic_pointer_castshared_from_this() 的结果转换为指向派生类的指针。

    【讨论】:

    【解决方案2】:

    这里的重要问题是为什么函数通过shared_ptr 接受参数。它是否在内部存储指针以供以后使用?它只在通话期间使用它吗?为什么调用者和被调用者之间的所有权被稀释了?

    如果要将堆栈分配的对象传递给函数,一些答案建议您提供无操作删除器,但如果函数实际上存储shared_ptr 以供以后使用,则可能是这样的情况当它到达它的时候,本地分配的对象不再在堆栈中,你触发了 UB。拥有无操作删除器shared_ptr 将允许调用,但语义将不正确。

    如果函数不存储shared_ptr 以供以后使用,那么导致该 API 的设计决策是什么?如果您可以更改函数(并且没有迫在眉睫的原因),使其通过引用接收参数,您将拥有一个更友好的界面,不会无缘无故强加shared_ptr

    如果最后您确定可以保证堆栈中的对象在该函数调用触发的整个过程期间都处于活动状态,那么并且只有在那时才使用无操作删除器。

    【讨论】:

    • 和我说的差不多,但你可能写得更清楚了。
    【解决方案3】:

    1) 不,如果没有shared_from_this,这并非不可能。您可以简单地构造一个带有无操作删除器的shared_ptr

    void do_nothing(MyClass*) {}
    
    void MyClass:memfun()
    {
        func(shared_ptr<MyClass>(this, do_nothing));
    }
    

    毕竟您实际上似乎并不需要shared_from_this,所以我将跳过您问题的下两部分。

    【讨论】:

    • boost 已经提供了一个无操作删除器,尽管由于某种原因它隐藏在 boost::serialization 中
    • 真的吗?在哪里?我们想知道这里,也想知道这个问题:stackoverflow.com/questions/2710765/… 和 Boost 错误跟踪器:svn.boost.org/trac/boost/ticket/1913
    • "带有 no-op 删除器的 shared_ptr" 每次我看到使用 no-op 删除器的建议时,我都会问:“使用 no-op 删除器有什么意义?智能指针在这里?普通指针就足够了吗?我不是说任何涉及无操作删除器的设计都是荒谬的,但任何此类设计都需要特定的理由。当然,当无法解释智能指针的使用时,应该使用法线来代替。
    • 是的,在某些情况下,您可能需要“即时”创建一个对象,持有者在完成后需要删除该对象,或者将其传递给一个已经存在的对象,在这种情况下,您不希望用户删除它。提供指针的人知道完成后应该使用它。该逻辑被放入“删除器”中。另一方只是做它被告知的事情并“调用”删除器,尽管是隐式的。
    • @curiousguy 调用者提供对象并且知道它应该在被调用者返回后继续存在。被调用者不知道这一点。
    【解决方案4】:

    如果你有一个自动存储的对象和一个需要 shared_ptr 的函数,并且你知道你的对象的生命周期对于函数的持续时间来说足够长,并且它不会在任何地方存储 shared_ptr,那么你可以通过它带有一个无操作删除器。

    这对于静态对象很有用。如果它确实有本地自动存储,你需要问自己为什么该函数使用 shared_ptr。它会存储它们吗?

    对于作为另一个引用计数对象成员的对象,还有另一个鲜为人知的 shared_ptr 构造函数。实际上,您可以使用外部对象的 shared_ptr 和内部对象的指针创建一个 shared_ptr。

    【讨论】:

    • "你知道你的对象的生命周期对于函数的持续时间来说足够长,并且它不会在任何地方存储 shared_ptr" 那么有什么意义这种情况下的“智能”指针?
    • 你调用的函数需要一个。也许它有时会被一个生命周期有限的人调用,即不能保证被调用者会坚持下去。
    • "您调用的函数需要一个。" 我知道,我想知道为什么。 “也许它有时会被一个生命周期有限的人调用,即不能保证被调用者会坚持下去。”我不明白。
    【解决方案5】:

    除了 David Rodríguez - dribeas,google 不推荐共享指针

    它内部维护着引用计数,所以让它正常工作,使用了InterlockedIncrement和InterlockedDecrement,这两个函数确实比普通的++和--慢。

    您应该检查此对象所有权是否确实需要与他人共享,根据我的经验,在大多数情况下可以避免共享指针。

    【讨论】:

    • 您没有回答所提出的问题。不应该避免使用智能指针。
    • “不推荐”在 CS 中并不成立。一切都有用处,在某些情况下 shared_ptr 不仅是一个不错的选择,而且是唯一的选择,或者至少是最正确的选择。除此之外 - 您的链接 404s
    猜你喜欢
    • 2023-03-05
    • 2016-11-12
    • 2021-11-29
    • 2011-09-22
    • 2018-05-23
    • 2011-11-16
    • 2021-03-27
    • 2017-01-27
    • 2011-10-31
    相关资源
    最近更新 更多