C++ `enable_shared_from_this` 有什么用处?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/712279/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-27 16:53:43  来源:igfitidea点击:

What is the usefulness of `enable_shared_from_this`?

c++boostboost-asiotr1

提问by fido

I ran across enable_shared_from_thiswhile reading the Boost.Asio examples and after reading the documentation I am still lost for how this should correctly be used. Can someone please give me an example and/or and explanation of when using this class makes sense.

我在enable_shared_from_this阅读 Boost.Asio 示例时遇到了问题,在阅读文档后,我仍然不知道如何正确使用它。有人可以给我一个例子和/或解释什么时候使用这个类是有意义的。

回答by 1800 INFORMATION

It enables you to get a valid shared_ptrinstance to this, when all you have is this. Without it, you would have no way of getting a shared_ptrto this, unless you already had one as a member. This example from the boost documentation for enable_shared_from_this:

它使你得到一个有效的shared_ptr实例this,当你已经是this。没有它,您将无法获得shared_ptrto this,除非您已经拥有一个成员。这个例子来自enable_shared_from_thisboost 文档

class Y: public enable_shared_from_this<Y>
{
public:

    shared_ptr<Y> f()
    {
        return shared_from_this();
    }
}

int main()
{
    shared_ptr<Y> p(new Y);
    shared_ptr<Y> q = p->f();
    assert(p == q);
    assert(!(p < q || q < p)); // p and q must share ownership
}

The method f()returns a valid shared_ptr, even though it had no member instance. Note that you cannot simply do this:

该方法f()返回一个有效的shared_ptr,即使它没有成员实例。请注意,您不能简单地这样做:

class Y: public enable_shared_from_this<Y>
{
public:

    shared_ptr<Y> f()
    {
        return shared_ptr<Y>(this);
    }
}

The shared pointer that this returned will have a different reference count from the "proper" one, and one of them will end up losing and holding a dangling reference when the object is deleted.

此返回的共享指针将具有与“正确”引用计数不同的引用计数,并且其中之一将在删除对象时最终丢失并持有悬空引用。

enable_shared_from_thishas become part of C++ 11 standard. You can also get it from there as well as from boost.

enable_shared_from_this已成为 C++ 11 标准的一部分。您也可以从那里以及从 boost 获取它。

回答by Artashes Aghajanyan

from Dr Dobbs article on weak pointers, I think this example is easier to understand (source: http://drdobbs.com/cpp/184402026):

从 Dobbs 博士关于弱指针的文章中,我认为这个例子更容易理解(来源:http: //drdobbs.com/cpp/184402026):

...code like this won't work correctly:

...这样的代码将无法正常工作:

int *ip = new int;
shared_ptr<int> sp1(ip);
shared_ptr<int> sp2(ip);

Neither of the two shared_ptrobjects knows about the other, so both will try to release the resource when they are destroyed. That usually leads to problems.

这两个shared_ptr对象都不知道对方,所以当它们被销毁时,它们都会尝试释放资源。这通常会导致问题。

Similarly, if a member function needs a shared_ptrobject that owns the object that it's being called on, it can't just create an object on the fly:

类似地,如果一个成员函数需要一个shared_ptr拥有它被调用的对象的对象,它不能只是动态地创建一个对象:

struct S
{
  shared_ptr<S> dangerous()
  {
     return shared_ptr<S>(this);   // don't do this!
  }
};

int main()
{
   shared_ptr<S> sp1(new S);
   shared_ptr<S> sp2 = sp1->dangerous();
   return 0;
}

This code has the same problem as the earlier example, although in a more subtle form. When it is constructed, the shared_ptr object sp1owns the newly allocated resource. The code inside the member function S::dangerousdoesn't know about that shared_ptrobject, so the shared_ptrobject that it returns is distinct from sp1. Copying the new shared_ptrobject to sp2doesn't help; when sp2goes out of scope, it will release the resource, and when sp1goes out of scope, it will release the resource again.

此代码与前面的示例存在相同的问题,但形式更微妙。当它被构造时,shared_ptr 对象sp1拥有新分配的资源。成员函数内部的代码S::dangerous不知道该shared_ptr对象,因此shared_ptr它返回的对象与sp1. 将新shared_ptr对象复制到sp2没有帮助;当sp2超出范围时,它会释放资源,当sp1超出范围时,它会再次释放资源。

The way to avoid this problem is to use the class template enable_shared_from_this. The template takes one template type argument, which is the name of the class that defines the managed resource. That class must, in turn, be derived publicly from the template; like this:

避免这个问题的方法是使用类模板enable_shared_from_this。模板采用一个模板类型参数,即定义托管资源的类的名称。反过来,该类必须从模板公开派生;像这样:

struct S : enable_shared_from_this<S>
{
  shared_ptr<S> not_dangerous()
  {
    return shared_from_this();
  }
};

int main()
{
   shared_ptr<S> sp1(new S);
   shared_ptr<S> sp2 = sp1->not_dangerous();
   return 0;
}

When you do this, keep in mind that the object on which you call shared_from_thismust be owned by a shared_ptrobject. This won't work:

执行此操作时,请记住,您调用的对象shared_from_this必须归某个shared_ptr对象所有。这行不通:

int main()
{
   S *p = new S;
   shared_ptr<S> sp2 = p->not_dangerous();     // don't do this
}

回答by mackenir

Here's my explanation, from a nuts and bolts perspective (top answer didn't 'click' with me). *Note that this is the result of investigating the source for shared_ptr and enable_shared_from_this that comes with Visual Studio 2012. Perhaps other compilers implement enable_shared_from_this differently...*

这是我的解释,从细节和螺栓的角度来看(最佳答案没有与我“点击”)。*请注意,这是对 Visual Studio 2012 附带的 shared_ptr 和 enable_shared_from_this 源进行调查的结果。也许其他编译器以不同的方式实现 enable_shared_from_this...*

enable_shared_from_this<T>adds a private weak_ptr<T>instance to Twhich holds the 'one true reference count' for the instance of T.

enable_shared_from_this<T>添加一个私有weak_ptr<T>实例,T其中包含 的实例的“一个真实引用计数T

So, when you first create a shared_ptr<T>onto a new T*, that T*'s internal weak_ptr gets initialized with a refcount of 1. The new shared_ptrbasically backs onto this weak_ptr.

因此,当您第一次shared_ptr<T>在新的 T* 上创建 a 时,该 T* 的内部 weak_ptr 将使用 1 的引用计数进行初始化。 newshared_ptr基本上支持 this weak_ptr

Tcan then, in its methods, call shared_from_thisto obtain an instance of shared_ptr<T>that backs onto the same internally stored reference count. This way, you always have one place where T*'s ref-count is stored rather than having multiple shared_ptrinstances that don't know about each other, and each think they are the shared_ptrthat is in charge of ref-counting Tand deleting it when their ref-count reaches zero.

T然后可以在其方法中调用shared_from_this以获取一个实例,shared_ptr<T>该实例返回到相同的内部存储引用计数。这样,您总是有一个地方T*存储 's ref-count,而不是有多个shared_ptr彼此不了解的实例,并且每个实例都认为它们shared_ptr负责 ref-countingT并在它们的 ref 时删除它-计数达到零。

回答by mchiasson

It's exactly the same in c++11 and later: It is to enable the ability to return thisas a shared pointer since thisgives you a raw pointer.

它在 c++11 及更高版本中完全相同:它是为了能够this作为共享指针返回,因为this它为您提供了一个原始指针。

in other word, it allows you to turn code like this

换句话说,它允许你像这样转换代码

class Node {
public:
    Node* getParent const() {
        if (m_parent) {
            return m_parent;
        } else {
            return this;
        }
    }

private:

    Node * m_parent = nullptr;
};           

into this:

进入这个:

class Node : std::enable_shared_from_this<Node> {
public:
    std::shared_ptr<Node> getParent const() {
        std::shared_ptr<Node> parent = m_parent.lock();
        if (parent) {
            return parent;
        } else {
            return shared_from_this();
        }
    }

private:

    std::weak_ptr<Node> m_parent;
};           

回答by blais

Note that using a boost::intrusive_ptr does not suffer from this problem. This is often a more convenient way to get around this issue.

请注意,使用 boost::intrusive_ptr 不会遇到此问题。这通常是解决此问题的更方便的方法。

回答by PetrH

Another way is to add a weak_ptr<Y> m_stubmember into the class Y. Then write:

另一种方法是将weak_ptr<Y> m_stub成员添加到class Y. 然后写:

shared_ptr<Y> Y::f()
{
    return m_stub.lock();
}

Useful when you cannot change the class you are deriving from (e.g. extending other people's library). Do not forget to initialize the member, e.g. by m_stub = shared_ptr<Y>(this), its is valid even during a constructor.

当您无法更改您派生的类时很有用(例如扩展其他人的库)。不要忘记初始化成员,例如 by m_stub = shared_ptr<Y>(this),即使在构造函数期间它也是有效的。

It is OK if there are more stubs like this one in inheritance hierarchy, it will not prevent destruction of the object.

如果继承层次结构中有更多这样的存根是可以的,它不会阻止对象的破坏。

Edit:As correctly pointed out by user nobar, the code would destroy Y object when the assignment is finished and temporary variables are destroyed. Therefore my answer is incorrect.

编辑:正如用户 nobar 正确指出的那样,当赋值完成并销毁临时变量时,代码将销毁 Y 对象。所以我的回答是错误的。