【问题标题】:C+11 Strategy Pattern with state带状态的 C+11 策略模式
【发布时间】:2014-04-29 09:35:20
【问题描述】:

本书Head First Design Patterns 中的Strategy Pattern 示例是在[here] 用C++ 编写的。我正在练习根据Effective GoF Patterns with C++11 and Boost 将其转换为 C++11 样式,如下所示。

嘎嘎行为:

struct Quack {
    static void quack()
    {
        std::cout << __FUNCTION__ << std::endl;
    }
};

struct MuteQuack {
    static void quack()
    {
        std::cout << __FUNCTION__ << std::endl;
    }
};

飞行行为:

struct FlyWithWings {
public:
    static void fly()
    {
        std::cout << __FUNCTION__ << std::endl;
    }
};

struct FlyNoWay {
public:
    static void fly()
    {
        std::cout << __FUNCTION__ << std::endl;
    }
};

Duck 层次结构:

class Duck
{
public:
    typedef std::function<void(void)> QUACK;
    typedef std::function<void(void)> FLY;

public:
    Duck(const QUACK &q, const FLY &f)
        : m_Quack(q), m_Fly(f) {}

    virtual ~Duck()
    {
    }

    void perform_quack()
    {
        m_Quack();
    }
    void perform_fly()
    {
        m_Fly();
    }

protected:
    QUACK   m_Quack;
    FLY     m_Fly;

private:
    Duck(const Duck&) = delete;
    Duck& operator=(const Duck&) = delete;
};

class MallardDuck
    : public Duck
{
public:
    MallardDuck()
        : Duck(&Quack::quack, &FlyWithWings::fly)
    {
    }
};

class PaintedDuck
    : public Duck
{
public:
    PaintedDuck()
        : Duck(&MuteQuack::quack, &FlyNoWay::fly)
    {
    }
};

到目前为止一切顺利,客户端运行良好。

int main()
{
    MallardDuck x1;
    x1.perform_quack();
    x1.perform_fly();

    PaintedDuck x2;
    x2.perform_quack();
    x2.perform_fly();

    return 0;
}

现在我想将新类RubberDuck 扩展到Duck 层次结构,RubberDuck 使用具有对象状态的新飞行行为FlyWithRocket。如下:

一种新的飞行行为:

class FlyWithRocket {
public:
    FlyWithRocket() : m_Energy(3) {}
    void fly()
    {
        if(m_Energy > 0)
        {
            fly_with_rocket();
            --m_Energy;
        }
        else
        {
            fly_out_of_energy();
        }
    }

private:
    void fly_with_rocket()
    {
        std::cout << __FUNCTION__ << std::endl;
    }
    void fly_out_of_energy()
    {
        std::cout << __FUNCTION__ << std::endl;
    }

    unsigned int m_Energy;
};

一个新的鸭子类型:

class RubberDuck
    : public Duck
{
public:
    RubberDuck()
        : Duck(&MuteQuack::quack, std::bind(&FlyWithRocket::fly, std::ref(m_flyrocket)))
        , m_flyrocket()
    {
    }
private:
    FlyWithRocket m_flyrocket;
};

从现在开始我想知道成员初始化顺序的规则。基础Duck 在成员m_flyrocket 之前初始化,但请注意基础Duck 使用绑定m_flyrocket 进行初始化,而绑定m_flyrocket 尚未初始化。 结果,当我在 VS2013 中运行它时,它在运行时没有任何问题。

但是代码真的不安全吗?如果没有,我该如何修改为更好的设计?

【问题讨论】:

  • 主要问题是“是否将未初始化的对象传递给构造函数未定义的行为?”。我猜在大多数情况下,只要您在构建过程中不访问它,它就应该起作用,但是这个答案表明它是未定义的行为,因此不安全:stackoverflow.com/a/22203006/104774

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


【解决方案1】:

它不安全,但除非您从基类构造函数调用m_Fly(),否则它不太可能中断。

不过,您可以通过以下任一方式轻松避免这种情况:

  1. 为基类构造函数提供一个虚拟或默认构造的std::function,并在派生类构造函数中将m_Fly 重新分配给您的绑定函子

    RubberDuck()
        : Duck(&MuteQuack::quack, std::function<void()>())
    {
        m_Fly = std::bind(&FlyWithRocket::fly, std::ref(m_flyrocket));
    }
    
  2. 使FlyWithRocket 本身成为一个函子(只需将void fly 重命名为void operator())并通过值传递它而不是保留一个私有成员(它将由m_Fly 函数对象拥有,您可以访问它如果需要,请通过std::function::target&lt;FlyWithRocket&gt;()

    class FlyWithRocket {
    public:
        FlyWithRocket() : m_Energy(3) {}
        void operator() () {
    // ...
    
    RubberDuck()
        : Duck(&MuteQuack::quack, FlyWithRocket()) {}
    

【讨论】:

    猜你喜欢
    • 2013-08-09
    • 2011-09-04
    • 1970-01-01
    • 1970-01-01
    • 2018-08-02
    • 1970-01-01
    • 2015-07-18
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多