我可以检查一下`shared_from_this`是否可以安全通话?

时间:2018-06-08 10:34:35

标签: c++ c++14 shared-ptr weak-ptr ownership-semantics

从继承自enable_shared_from_this的类型中调用shared_from_this时,如果this对象当前未保留shared_ptr,则可能会发生非常糟糕的事情(TM)通常,应用程序崩溃)。是否可以在C ++ 14(而不是17)中检查它是否安全?

编辑:使用异常或尝试/捕获。

3 个答案:

答案 0 :(得分:1)

这是一个实现细节,但你可以用内部的朋友声明做一些讨厌的事情:

template<typename _Tp1>
friend void
__enable_shared_from_this_helper(const __shared_count<>& __pn,
                 const enable_shared_from_this* __pe,
                 const _Tp1* __px) noexcept

使用_Tp1实现你自己的版本作为weak_ptr&lt;&gt; *,返回弱指针[实际上不完全__px是一个const指针,所以你需要一个额外的间接来丢失const,或者如果你是脏的话把它扔掉!] 。将其全部包含在您从中派生的类中,而不是enable_shared_from_this:

#if >= C++17
using enable_shared_from_this_c17 = enable_shared_from_this;
#else

template<typename _Tp>
enable_shared_from_this_c17: public enable_shared_from_this<_Tp>
{

  weak_ptr<_Tp> weak_from_this()
  {
    weak_ptr<_Tp> rv; auto rv2 = &rv;
    __enable_shared_from_this_helper(*(const __shared_count<>*)nullptr, this, &rv2);
    return rv;
  }
}
#endif

现在,你在c ++ 14中有一个weak_from_this()的实现。是的,这是一个令人讨厌的污泥,但直到你升级到17。

或者,只是抓住异常!

第三种选择 - 添加一个实例化模板包装器,用于设置&#34;构造&#34;在enable_shared_from_this的包装器中包装shared_from_this(),因此它失败直到已经设置了构造的标志。

enable_shared_from_this
    safe_shared_from_this
        your interfaces
            your_implementation
                constructed<your_implementation>

当然,如果在没有立即分配给shared_ptr的情况下使用该类,这是不完美的。

答案 1 :(得分:1)

如果程序中的其他任何对象都可以访问对象的原始this指针,那么您使用shared_ptr的方式是完全错误的。

任何要在对象外部使用的指向对象的指针都必须包装在shared_ptr<YourObject>值实例中,没有例外。

答案 2 :(得分:0)

如果this没有shared_ptr,那么你就无法做任何事情。来自cppreference:It is permitted to call shared_from_this only on a previously shared object, i.e. on an object managed by std::shared_ptr.这是一个例子:

#include <memory>

class MyClass;

std::shared_ptr<MyClass> global;

void resetGlobal()
{
    global = std::make_shared<MyClass>();
}

class MyClass : public std::enable_shared_from_this<MyClass>
{
public:
    void use()
    {
        resetGlobal();
        shared_from_this();
    }
};

int main()
{
    global = std::make_shared<MyClass>();
    global->use();
    return 0;
}

use方法的开头shared_from_this应该没问题(除了在没有互斥的多线程环境中)但是在调用任何可能在其他地方改变shared_ptr的函数之后你无法确定。调用resetGlobal后,函数this被简单地销毁,对它的任何访问都是未定义的行为,因此可能导致分段错误。

确保整个方法调用shared_from_this有效的唯一方法是在通话开始时制作临时shared_ptr

void use()
{
    std::shared_ptr<MyClass> tmp = shared_from_this();
    resetGlobal();
    shared_from_this();
}

{
    std::shared_ptr<MyClass> tmp = global;
    global->use();
}