C++11状态模式与策略模式

6

以下是一则关于《Head First设计模式》书中的策略模式C++示例,代码可在[此处]查看。我正按照C++11和Boost实现GoF设计模式的要求进行练习,将其转换为C++11风格。

Quack行为:

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

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

Fly 行为:

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

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

鸭子层次结构:
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;
};

一个新的Duck类型:

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进行初始化的。

在我在VS2013中运行它时,结果没有出现任何错误。

但是这段代码实际上是否安全?如果不安全,我该如何修改以获得更好的设计?


1
主要问题是“将未初始化的对象传递给构造函数是否是未定义行为?”我猜在大多数情况下,只要在构造期间不访问它,这应该是有效的,但是这个答案表明这是未定义的行为,因此不安全:https://dev59.com/CX3aa4cB1Zd3GeqPakGi#22203006 - stefaanv
1个回答

7

这并不安全,但除非你从基类构造函数中调用m_Fly(),否则不太可能出错。

但你可以很容易地避免这种情况,方法如下:

  1. giving the base class constructor a dummy or default-constructed std::function, and re-assigning m_Fly to your bind functor in the derived class constructor

    RubberDuck()
        : Duck(&MuteQuack::quack, std::function<void()>())
    {
        m_Fly = std::bind(&FlyWithRocket::fly, std::ref(m_flyrocket));
    }
    
  2. making FlyWithRocket a functor itself (just rename void fly to void operator()) and passing it by value instead of keeping a private member (it will be owned by the m_Fly function object, and you can access it via std::function::target<FlyWithRocket>() if you need)

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

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接