在没有指针的情况下在 C++ 中实现策略模式

Implement Strategy Pattern in C++ without Pointers

我的目标是拥有 Strategy Pattern 的 C++ 实现,无需手动分配内存。从概念上讲,我觉得在我给出的例子中没有必要这样做。此外,手动内存管理容易出错。请记住,我的示例只是一个 MWE,实际上所有对象都更加复杂,手动内存管理经常会引入错误。


在下面的例子中,class Base 需要被赋予一个 Strategy 来构建,它将用于在其方法 [=13= 中产生输出].当然也可以给一个Strategy,因为那个class是抽象的,所以这个论点必须是引用。由于我的上述原因,它是 而不是 指针。

下面的代码可以编译,但是只能因为const已经在几个地方添加了。

class Strategy {
public:
    virtual int decision() const = 0;
};

class AlwaysZero : public Strategy {
public:
    int decision() const { return 0; }
};


class Base {
private:
    Strategy const &strategy;
public:
    Base(Strategy const &s) : strategy(s) {}

    int getStrategyDecision() {
        return strategy.decision();
    }
};


class Main : public Base {
public:
    Main() : Base(AlwaysZero()) {}
};


int main ()
{
    Main invoker;
    return invoker.getStrategyDecision();
}

如果删除 const 限定符,它会失败,因为在 Main 的构造函数中,将临时对象 AlwaysZero() 传递给 [=11= 的构造函数的引用参数] 是非法的。

我能理解那个错误,但是

  1. 为什么当一切都是 const 时它仍然有效?这似乎表明它应该在没有 const 的情况下同样有效,我只是遗漏了一些东西。
  2. 我什至不想要一个“temporary”对象,我必须这样做的唯一原因是因为我不能传递 Strategy 本身,因为 class 是抽象的。

同样,我知道我可以通过使变量 strategy 成为指向 Strategy 的指针并将 new AlwaysZero() 传递给 Base 构造函数来解决这个问题。如上所述,这是我不想要的


我的问题是双重的。首先,我想知道为什么我的示例在没有 const 的情况下无法编译。我想了解这里发生了什么。

其次,我想知道是否可以修改此示例,使 strategy 不再是 const(并且有效),但不使用任何类型的指针。只有当第二个问题的答案是 "no" 时,我才会开始寻找巧妙的方法来避免这个问题(例如评论中建议的智能指针)。

PS:我使用 g++ 作为编译器。这可能重要也可能不重要。

您的代码可以编译的原因是 const 引用可以延长临时文件的寿命,但非 const 引用不会。有关详细说明,请参阅 https://herbsutter.com/2008/01/01/gotw-88-a-candidate-for-the-most-important-const/

至于为什么 C++ 标准说非常量引用不会延长临时对象的生命周期。正如 here, non-const references can't be bound to temporaries in the first place. See here 所解释的一个简单的原因示例。

所以,你的技术应该没问题。但是如果你想要一些其他的选择...

策略模板

template<typename T>
class Base {
private:
    T strategy;
public:
    Base(const T& s) : strategy(s) {}

    int getStrategyDecision() {
        return strategy.decision();
    }
};

class Main : public Base<AlwaysZero> {
public:
    Main() : Base(AlwaysZero()) {}
};

非拥有指针

无需手动内存管理即可使用指针。只要它们是非拥有指针。

class Base {
private:
    Strategy *strategy;
protected:
    void setStrategy(Strategy& s) { strategy = &s; }
public:
    int getStrategyDecision() {
        return strategy->decision();
    }
};

class Main : public Base {
    AlwaysZero my_strategy;
public:
    Main()  { setStrategy(my_strategy); }
};