【问题标题】:Should safe pointers be used in strategy pattern?策略模式中应该使用安全指针吗?
【发布时间】:2015-02-06 11:24:23
【问题描述】:

给定一个典型的策略模式

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

【问题讨论】:

  • 为什么Context 不通过reference 获取Strategy?那么,没有歧义!

标签: c++ c++11 design-patterns strategy-pattern


【解决方案1】:

设计取决于实施者。

请注意,在您的示例中,您引用了使用非 C++11 指针搞砸策略模式的不同方法。

直接回答您的问题:

是的,您应该在策略模式中使用智能指针。

进一步回答问题:

您应该尽可能使用智能指针。

原因是智能指针在内存所有权政策方面实际上是自我记录的,因此您摆脱了“如果没有好的文档”的一些缺点。

考虑到您为 Context 类公开的原型,您可以告诉用户您的期望是什么:

  • unique_ptr 如果您希望用户将内存所有权传递给您
  • shared_ptr 如果您希望在多个所有者上使用相同的策略实现
  • weak_ptr 如果您希望用户处理内存管理

什么更安全,由您决定。但是,您可以告诉用户 Context 可以与其他 Context 共享它的具体策略,或者每个 Context 有 1 个具体策略。

作为一种设计方法,我建议使用 1 Strategy / Context (so unique_ptr),因为您的具体策略最终可能会包含一些唯一 / context 的内部变量,并且从那里开始事情会变得复杂。

【讨论】:

    【解决方案2】:

    你做错了。

    鉴于std::function,您刚刚编写的所有内容都已完全过时,您应该只使用std::function&lt;int()&gt; 和一些lambda。

    【讨论】:

    • 您不需要将 lambdas 与 std::function 一起使用,它只允许您这样做。当然,它也避免混淆所有权语义。
    • 虽然我很感谢您的意见,但如果您能解释为什么使用std::function 是一种更好的方法而不是仅仅谴责我的问题,那就太好了。
    • @Daniel 使用std::function 将避免所有权问题,因为您不再需要通过指向 base 的指针传递参数。只需允许 std::function 包装你的函子,它就会代表你处理内存管理。
    • @Daniel:因为std::function 以一种完全通用的方式完全替换了您的基类和所有派生类,并使添加新策略和为您处理所有所有权变得无限容易。换句话说,std::function 是预先编写的,更安全、更快、更通用、更灵活、更易于使用。所以基本上各方面都比较好。
    • 如果我们需要非 const 类会发生什么?例如strategy 将适用于类字段,例如具有静态或全局变量的函数。
    【解决方案3】:

    这在很大程度上取决于 Strategy 对象的真正目的是应该在各种 Context 对象之间共享还是归它们所有。

    至少当您使用共享或唯一 ptr 时,您清楚地定义了您的意图。仅当您要“查看”其他一些对象时才应使用“原始”指针(您不共享或拥有它 - 并且您确定指向对象不会比指向对象的寿命更长)。

    【讨论】:

      猜你喜欢
      • 2011-09-04
      • 1970-01-01
      • 1970-01-01
      • 2019-12-27
      • 2015-10-16
      • 1970-01-01
      • 2017-07-15
      • 2012-07-02
      相关资源
      最近更新 更多