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


当前回答

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

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

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

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

类似地,如果一个成员函数需要一个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 sp1 owns the newly allocated resource. The code inside the member function S::dangerous doesn't know about that shared_ptr object, so the shared_ptr object that it returns is distinct from sp1. Copying the new shared_ptr object to sp2 doesn't help; when sp2 goes out of scope, it will release the resource, and when sp1 goes out of scope, it will release the resource again.

避免这个问题的方法是使用类模板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;
}

在执行此操作时,请记住调用shared_from_this的对象必须属于shared_ptr对象。这行不通:

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

其他回答

它使您能够获得一个有效的shared_ptr实例,当您只有这个时。没有它,你将无法获得一个shared_ptr到this,除非你已经有一个作为成员。下面的例子来自enable_shared_from_this的boost文档:

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
}

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

class Y: public enable_shared_from_this<Y>
{
public:

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

返回的共享指针将具有不同于“正确”的引用计数,当对象被删除时,其中一个指针将丢失并保留一个悬空引用。

enable_shared_from_this已经成为c++ 11标准的一部分。你也可以从那里和从boost得到它。

以下是我的解释,从具体的角度来看(上面的答案对我来说不“合适”)。*注意,这是调查Visual Studio 2012附带的shared_ptr和enable_shared_from_this的源代码的结果。也许其他编译器实现enable_shared_from_this的方式不同…*

enable_shared_from_this<T>为T添加了一个私有的weak_ptr<T>实例,该实例保存了T实例的“一个真实引用计数”。

因此,当你第一次在一个新的T*上创建一个shared_ptr<T>时,这个T*的内部weak_ptr初始化为1的refcount。新的shared_ptr基本上回到了这个weak_ptr。

然后,T可以在其方法中调用shared_from_this来获得shared_ptr<T>的实例,该实例返回到相同的内部存储引用计数。这样,你总是有一个地方存储T*的引用计数,而不是有多个不知道彼此的shared_ptr实例,每个实例都认为他们是shared_ptr,负责引用计数T,并在他们的引用计数为零时删除它。

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

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

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

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

类似地,如果一个成员函数需要一个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 sp1 owns the newly allocated resource. The code inside the member function S::dangerous doesn't know about that shared_ptr object, so the shared_ptr object that it returns is distinct from sp1. Copying the new shared_ptr object to sp2 doesn't help; when sp2 goes out of scope, it will release the resource, and when sp1 goes out of scope, it will release the resource again.

避免这个问题的方法是使用类模板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;
}

在执行此操作时,请记住调用shared_from_this的对象必须属于shared_ptr对象。这行不通:

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

这在c++11及以后的版本中是完全相同的:它是为了启用作为共享指针返回this的能力,因为这给了你一个原始指针。

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

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

private:

    Node * m_parent = nullptr;
};           

到这个:

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;
};           

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