应该在策略模式中使用安全指针吗?
Should safe pointers be used in strategy pattern?
给定一个典型的策略模式
class Strategy
{
public:
virtual int execute() const = 0;
}
class StrategyA : public Strategy
{
public:
int execute() const override;
}
class StrategyB : public Strategy
{
public:
int execute() const override;
}
我相信 'pre-C++11' 实现上下文 class 的方式类似于
class ContextRaw
{
public:
ContextRaw(Strategy* the_strategy);
~ContextRaw(); // Should this delete the_strategy_?
int execute() const;
private:
Strategy* the_strategy_;
}
对我来说,在这个设计中,不清楚 Context
是否应该对 Strategy
负责,除非有明确的文档说明,否则可能会发生不好的事情
void trouble()
{
StrategyA a_concrete_strategy;
ContextRaw a_context(&a_concrete_strategy); // Oops, Context may try to delete stack variable
}
void more_trouble()
{
Strategy* a_concrete_strategy = new StrategyA;
ContextRaw* a_context = new ContextRaw(a_concrete_strategy);
ContextRaw* another_context = new ContextRaw(a_concrete_strategy);
delete a_context;
std::cout << another_context.execute() << std::endl; // Oops, the_strategy is deleted
}
鉴于安全指针,现在是否应该注入安全指针并让 Context
获得 Strategy
的所有权?
class ContextUnique
{
public:
ContextUnique() = delete;
ContextUnique(std::unique_ptr<Strategy> the_strategy);
~ContextUnique();
int execute() const;
private:
std::unique_ptr<Strategy> the_strategy_;
}
或者 Strategy
是否可以在不同 Context
之间共享?
class ContextShared
{
public:
ContextShared() = delete;
ContextShared(std::shared_ptr<Strategy> the_strategy);
~ContextShared();
int execute() const;
private:
std::shared_ptr<Strategy> the_strategy_;
}
这种设计当然引入了它自己的问题,特别是只有动态分配的 Strategy
可以注入到 Context
.
这在很大程度上取决于 Strategy 对象的真正用途是什么,它们是应该在各种 Context 对象之间共享还是由它们拥有。
至少当你使用shared或unique ptr时,你清楚地定义了你的意图。只有当你要 "view" 一些其他对象时,你才应该使用 "raw" 指针(你不共享也不拥有它 - 你确信指向对象不会比指向的对象长寿) .
设计由实施者决定。
请注意,在您的示例中,您引用了使用非 C++11 指针搞砸策略模式的不同方式。
直接回答你的问题:
是的,你应该在策略模式中使用智能指针。
进一步回答问题:
您应该尽可能使用智能指针。
原因是智能指针在内存所有权策略方面实际上是自记录的,因此您摆脱了 "If no good documentation" 的一些缺点。
考虑到您为 Context class 公开的原型,您可以告诉用户您的期望是什么:
- unique_ptr 如果您希望用户将内存所有权传递给您
- shared_ptr 如果您希望对多个所有者使用相同的策略实施
- weak_ptr 如果你想让用户处理内存管理
什么更安全,由您决定。但是,您可以告诉用户上下文可以与其他上下文共享它的具体策略,或者每个上下文有 1 个具体策略。
作为一种设计方法,我建议使用 1 个 Strategy / Context(所以 unique_ptr),因为您的具体策略最终可能会有一些独特的内部变量 / context,事情会变得复杂从那以后。
你做错了。
鉴于 std::function
,您刚刚编写的所有内容都已完全过时,您应该只使用 std::function<int()>
和一些 lambda。
给定一个典型的策略模式
class Strategy
{
public:
virtual int execute() const = 0;
}
class StrategyA : public Strategy
{
public:
int execute() const override;
}
class StrategyB : public Strategy
{
public:
int execute() const override;
}
我相信 'pre-C++11' 实现上下文 class 的方式类似于
class ContextRaw
{
public:
ContextRaw(Strategy* the_strategy);
~ContextRaw(); // Should this delete the_strategy_?
int execute() const;
private:
Strategy* the_strategy_;
}
对我来说,在这个设计中,不清楚 Context
是否应该对 Strategy
负责,除非有明确的文档说明,否则可能会发生不好的事情
void trouble()
{
StrategyA a_concrete_strategy;
ContextRaw a_context(&a_concrete_strategy); // Oops, Context may try to delete stack variable
}
void more_trouble()
{
Strategy* a_concrete_strategy = new StrategyA;
ContextRaw* a_context = new ContextRaw(a_concrete_strategy);
ContextRaw* another_context = new ContextRaw(a_concrete_strategy);
delete a_context;
std::cout << another_context.execute() << std::endl; // Oops, the_strategy is deleted
}
鉴于安全指针,现在是否应该注入安全指针并让 Context
获得 Strategy
的所有权?
class ContextUnique
{
public:
ContextUnique() = delete;
ContextUnique(std::unique_ptr<Strategy> the_strategy);
~ContextUnique();
int execute() const;
private:
std::unique_ptr<Strategy> the_strategy_;
}
或者 Strategy
是否可以在不同 Context
之间共享?
class ContextShared
{
public:
ContextShared() = delete;
ContextShared(std::shared_ptr<Strategy> the_strategy);
~ContextShared();
int execute() const;
private:
std::shared_ptr<Strategy> the_strategy_;
}
这种设计当然引入了它自己的问题,特别是只有动态分配的 Strategy
可以注入到 Context
.
这在很大程度上取决于 Strategy 对象的真正用途是什么,它们是应该在各种 Context 对象之间共享还是由它们拥有。
至少当你使用shared或unique ptr时,你清楚地定义了你的意图。只有当你要 "view" 一些其他对象时,你才应该使用 "raw" 指针(你不共享也不拥有它 - 你确信指向对象不会比指向的对象长寿) .
设计由实施者决定。
请注意,在您的示例中,您引用了使用非 C++11 指针搞砸策略模式的不同方式。
直接回答你的问题:
是的,你应该在策略模式中使用智能指针。
进一步回答问题:
您应该尽可能使用智能指针。
原因是智能指针在内存所有权策略方面实际上是自记录的,因此您摆脱了 "If no good documentation" 的一些缺点。
考虑到您为 Context class 公开的原型,您可以告诉用户您的期望是什么:
- unique_ptr 如果您希望用户将内存所有权传递给您
- shared_ptr 如果您希望对多个所有者使用相同的策略实施
- weak_ptr 如果你想让用户处理内存管理
什么更安全,由您决定。但是,您可以告诉用户上下文可以与其他上下文共享它的具体策略,或者每个上下文有 1 个具体策略。
作为一种设计方法,我建议使用 1 个 Strategy / Context(所以 unique_ptr),因为您的具体策略最终可能会有一些独特的内部变量 / context,事情会变得复杂从那以后。
你做错了。
鉴于 std::function
,您刚刚编写的所有内容都已完全过时,您应该只使用 std::function<int()>
和一些 lambda。