安全enable_shared_from_this用法

safe enable_shared_from_this usage

本文关键字:this 用法 from shared enable 安全      更新时间:2023-10-16

请考虑以下简化示例:

#include "boost/shared_ptr.hpp"
#include "boost/smart_ptr/enable_shared_from_this.hpp"
using namespace boost;
class State : public boost::enable_shared_from_this<State>
{
public:
    shared_ptr<State> GetSelf()
    {
        return shared_from_this();
    };
    // returns following state
    virtual shared_ptr<State> Execute() = 0;
};
template<typename T>
shared_ptr<T> Create()
{
    return shared_ptr<T>(new T);
}
class MyState2;
class MyState1 : public State
{
    virtual shared_ptr<State> Execute()
    {
            if (change of state)
          return Create<MyState2>();
            else
              return GetSelf();
    };
};
class MyState2 : public State
{
    virtual shared_ptr<State> Execute()
    {
            if (change of state)
          return Create<MyState1>();
            else
              return GetSelf();
    }; 
};
int main(int argc, char* argv[])
{
    shared_ptr<State> pCurrentState = Create<MyState1>();  // <-- OK
//  State* pCurrentState(new MyState1);                    // <-- Runtime error
    while (...)
      pCurrentState = pCurrentState->Execute();
    return 0;
}

state类是"框架"的一部分。它是轻量级状态机中用户定义状态的基础,其中每个状态返回其跟随状态。如果状态没有改变,它将返回自身。框架用户可以任意地从类State派生。显然,不允许创建派生类的"不受控制"实例(如果调用GetSelf()将导致运行时错误)。为了给用户指明正确的方向,提供了一个Create()函数模板来创建"受控"实例。既然我想使使用尽可能万无一失,我怎么能确保用户不会创建派生状态的任何实例不受shared_ptr控制?或者至少给出一个有意义的错误消息(在编译时?)。

谢谢你的帮助!

防止非托管实例的最简单方法是使每个派生类的构造函数为私有。你的工厂函数需要是它所创建的类的友元或成员;如果你想保留你的工厂模板,那么它需要在派生类之前声明,以使它成为友元:

template<typename T>
shared_ptr<T> Create()
{
    return shared_ptr<T>(new T);
}
class Derrived : public Base
{
private:
    friend shared_ptr<Derrived> Create<Derrived>();
    Derrived() {}
    // Client implementation goes here
};

然而,我通常会建议您不要强制以特定的方式管理类。只有当类本身要求由共享指针管理才能正常工作时(也就是说,由于某种原因,它们需要从其成员函数中调用shared_from_this()),才应该这样做。否则,只要组织你的框架,让它使用共享指针,任何使用框架的人都必须这样做,而不需要奇怪的强制机制。根本不需要GetSelf(),因为如果您可以访问对象,您将始终有一个可用的共享指针进行复制,并且不需要enable_shared_from_this,除非类需要在内部访问共享指针(这是相当不寻常的事情)。

更新:在你的用例中,我认为基类不可能强制使用共享指针(但如果我错了,请纠正我)。在调用需要共享指针的函数时,强制使用共享指针可能是有意义的。您将能够创建非托管对象,但不会对它们做任何危险的事情。

class State : public boost::enable_shared_from_this<State>
{
public:
    // returns following state
    friend shared_ptr<State> Execute(shared_ptr<State> state) {
        return state->Execute();
    }
private:
    virtual shared_ptr<State> Execute() = 0;
};
class MyState : public State
{
    shared_ptr<State> Execute() {return shared_from_this();}
};
int main(int argc, char* argv[])
{
    shared_ptr<State> state1(new MyState);  // <-- OK
    State * state2(new MyState);            // <-- OK, but can't be used
    Execute(state1);                        // <-- OK
    // Execute(state2);                     // <-- Error
    // state2->Execute();                   // <-- Error
}

一般情况下没有办法做你想做的。

如果Base不是一个基类(即:他们不应该从它派生),那么它很容易。将构造函数设为工厂函数,并将构造函数设为私有。因此,它们必须经过您的工厂,因此您可以确保他们使用shared_ptr来包装它们。

然而,如果您允许用户从它们派生类,那么所有的赌注都取消了。所以你需要考虑两件事:

  1. 真的这里需要enable_shared_from_this吗?
  2. I 真的需要允许继承吗?

1:你发布的代码并没有真正给出为什么你需要使用enable_shared_from_this的原因。您唯一使用它的地方是在一个函数中,该函数向该实例返回一个shared_ptr。那它要还给谁呢?该对象的用户。谁只能创建一个实例…具有 shared_ptr .

简而言之:你给他们一些他们已经有机会接触到的东西。函数是无用的。

好吧,它是无用的,除非你违反正确使用shared_ptr。一般来说,一旦将指针包装在shared_ptr中,要么保持这种方式,要么将其转换为weak_ptr。你几乎从不给别人一个指向那个对象的裸指针。如果你这样做了,那么这应该是一个明确的信号,即不是来存储指针,或者期望以任何方式、形状或形式保持指针的任何形式的所有权。

因此,他们能够从裸指针中获得shared_ptr是糟糕的设计。

enable_shared_from_this用于单个使用指向shared_ptr保存的对象的裸指针,这些对象无法避免:this。它的存在是因为成员函数可能需要调用带有指向该对象的指针的外部函数,但是该外部函数采用shared_ptrweak_ptr而不是裸指针。这不是一种允许糟糕设计的方式。

2:你正在构建一个状态机。那么,为什么基类State需要从enable_shared_from_this派生呢?如果派生类的Execute方法将返回自身,则由派生类来处理它。基类不应该负责提供这种功能。它当然不应该由公共函数提供。

简而言之,如果每个派生类想要返回指向自身的指针,则需要继承enable_shared_from_this。因此,保持这些东西的正确是用户的责任,而不是你的。