用指针实现C ++中的策略模式

时间:2017-05-04 08:54:55

标签: c++ g++ abstract-class strategy-pattern

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

在下面的示例中,需要为类Base提供要构造的Strategy,它将用于在其方法getStrategyDecision()中生成输出。当然,它可以给予Strategy因为该类是抽象的,所以这个参数必须是一个引用。由于上述原因,不是指针。

以下代码执行编译,但,因为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()传递给Base的构造函数的引用参数是非法。

我能理解这个错误,但是

  1. 为什么一切都在const时才有效?这似乎表明它应该在没有const的情况下同样有效,我只是遗漏了一些东西。
  2. 我甚至不想要一个“临时”对象,我必须这样做的唯一原因是因为我不能传递Strategy本身,因为该类是抽象的。
  3. 同样,我知道我可以通过将变量strategy指向Strategy并将new AlwaysZero()传递给Base构造函数来解决此问题。这就是我不想要的,如上所述。

    我的问题是双重的。首先,我想知道为什么我的例子编译,当它没有const时。我想了解这里发生了什么。

    其次,我想知道是否可以修改此示例,以便strategy不再是const(并且它可以工作),但不使用任何类型的指针。只有当第二个问题的答案是“no”时,我才会开始寻找避免问题的聪明方法(例如智能指针,如评论中所建议的那样)。

    PS:我使用g ++作为编译器。它可能会或可能不重要。

1 个答案:

答案 0 :(得分:3)

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

至于为什么 C ++标准说非const引用并没有延长临时的寿命。正如here所解释的那样,非const引用不能首先绑定到临时值。有关原因的简单示例,请参阅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); }
};