请考虑以下简化示例:
#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;
}
国家级是“框架”的一部分。它是轻量级状态机中用户定义状态的基础,其中每个状态返回其跟随者状态。如果状态没有改变,它将自行返回。框架用户可以任意地从类州中掠过。显然,不允许创建“不受控制的”类的实例(如果调用了GetSelf(),则会导致运行时错误)。为了将用户指向正确的方向,提供了Create()函数模板来创建“受控”实例。由于我希望尽可能简化使用,我如何确保用户不会创建任何不在shared_ptr控制下的原始状态实例?或者至少给出一个有意义的错误信息(在编译时?),如果他这样做的话。
感谢您的帮助!
答案 0 :(得分:2)
无法在常规案例中执行您想要的操作。
如果Base
不是基类(即:它们不应该来自它),那么它很容易。使您的构造函数工厂函数并使构造函数私有。因此,他们必须通过您的工厂,因此您可以确保他们使用shared_ptr
来包装它们。
但是,如果您允许用户从中派生类,则所有投注均已关闭。所以你需要考虑两件事:
1:您发布的代码并没有真正说明您需要完全使用enable_shared_from_this
的原因。您使用它的唯一地方是在一个向该实例返回enable_shared_from_this
的函数中。好吧,谁回来了?该对象的用户。谁只能通过 shared_ptr
来创建实例。
简而言之:你给他们一些他们必须已经拥有的东西。这个功能没用。
嗯,除非你违反正确使用shared_ptr
,否则它没用。通常,一旦您将指针包裹在shared_ptr
中,您可以保持这种方式,也可以从中shared_ptr
。你几乎从不给某人一个指向该对象的裸指针。如果你这样做,那么它应该是一个明确的信号,表明他们不来存储指针,或者希望以任何方式,形状或形式保持对它的任何形式的所有权。
因此,他们能够从裸指针获得weak_ptr
是糟糕的设计。
shared_ptr
用于单个使用裸指针到enable_shared_from_this
持有的对象,这是您无法避免的:shared_ptr
。它的存在是因为成员函数可能需要使用指向该对象的指针调用外部函数,但该外部函数需要this
或shared_ptr
而不是裸指针。它并不意味着允许糟糕的设计。
2:你正在构建状态机。那么为什么基类weak_ptr
需要从State
派生?如果派生类的enable_shared_from_this
方法将返回自身,则由该派生类来处理它。提供它不应该是基类的责任。 当然不应由公共职能部门提供。
简而言之,如果每个派生类想要返回指向自身的指针,则需要从Execute
继承。因此,用户的责任是保持这些事情,而不是你的。
答案 1 :(得分:2)
防止非托管实例的最简单方法是将每个派生类的构造函数设为私有。您的工厂职能需要是朋友或其创建的班级成员;如果你想保留你的工厂模板,那么它需要在派生类之前声明才能使它成为朋友:
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
}