2014-04-29 17 views
5

Przykład Strategy Pattern z książki, Head First Design Patterns, został napisany w języku C++ pod numerem [here]. Ćwiczę, aby przekształcić go w stylu C++ 11 zgodnie z Effective GoF Patterns with C++11 and Boost, jak pokazano poniżej.Wzór strategii C + 11 ze stanem

Znachor zachowanie:

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

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

Fly zachowanie:

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

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

Duck hierarchia:

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) 
    { 
    } 
}; 

Jak na razie dobrze, klient działa dobrze.

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

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

    return 0; 
} 

Teraz chciałbym przedłużyć do nowej klasy RubberDuck do Duck hierarchii, a RubberDuck wykorzystuje nowe zachowanie muchy FlyWithRocket który ma stan obiektu. W następujący sposób:

Nowa Fly zachowanie:

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

Nowa Duck typ:

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

Od teraz zastanawiam się, że zasada kolejności inicjalizacji użytkownik . Podstawa Duck inicjuje przed elementem m_flyrocket, ale należy pamiętać, że podstawa Duck została zainicjowana wiązaniem m_flyrocket, które nie zostało jeszcze zainicjowane. W wyniku uruchomienia w VS2013 działa to bezbłędnie w czasie wykonywania.

Ale czy kod właściwie nie jest bezpieczny? Jeśli nie, jak mogę zmodyfikować projekt na lepszy?

+1

Głównym pytaniem jest „przechodzi non-zainicjowany obiektu do konstruktora niezdefiniowanej zachowanie? ".Przypuszczam, że w większości przypadków powinno to działać tak długo, jak nie masz dostępu do niego podczas budowy, ale ta odpowiedź pokazuje, że jest to niezdefiniowane zachowanie, więc nie jest bezpieczne: http://stackoverflow.com/a/22203006/104774 – stefaanv

Odpowiedz

6

To nie jest bezpieczne, ale jest mało prawdopodobne, że się zerwie, chyba że zadzwonisz pod numer m_Fly() z konstruktora klasy podstawowej.

można łatwo uniknąć tego jednak przez:

  1. dając konstruktora klasy bazowej manekina lub domyślne zbudowane std::function i ponowne przypisanie m_Fly do wiązania funktora w pochodzącej konstruktora klasy

    RubberDuck() 
        : Duck(&MuteQuack::quack, std::function<void()>()) 
    { 
        m_Fly = std::bind(&FlyWithRocket::fly, std::ref(m_flyrocket)); 
    } 
    
  2. dokonywania FlyWithRocket sam funktor (tylko zmienić nazwę void fly do void operator()) i przepuszczenie go przez wartość zamiast trzymać prywatną członk er (będzie własnością obiektu m_Fly funkcji, można uzyskać do niego dostęp za pośrednictwem std::function::target<FlyWithRocket>() jeśli trzeba)

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

Oba są doskonałe! – cbel