2016-11-23 91 views
4

在C++類中處理狀態的最有效和最現代的方式是什麼? 目前我正在使用多個布爾,但越多我說我更難以維護。例如,如果我有一個名爲VideoPlayer的類,並且它有四個定義不同狀態的布爾值。如果我們爲每個布爾添加set方法,我們需要取消設置所有其他布爾。在C++類中處理多個狀態

class VideoPlayer 
{ 

public: 
    void play() 
    { 
     play_ = true; 
     pause_ = false; 
     stop_ = false; 
     reset_ = false; 
    } 

    void stop() 
    { 
     stop_ = true; 
     play_ = false; 
     pause_ = false; 
     reset_ = false; 
    } 

private: 
    bool play_; 
    bool pause_; 
    bool stop_; 
    bool reset_; 
}; 
+0

也許一個小的有限狀態機將是適當的。 –

+3

看起來像枚舉可能在這裏做的工作。 – sjwarner

+0

如果您使用的是C++ 11,請改用'enum class'。 – Dai

回答

8

您的設計從它易於遭受是在一個糟糕的狀態(例如,如果什麼都stop_play_都是true

您應該使用enum定義僅1罐set of finite-states

C++的enum類型有點不同於Swift,Java和C#的enum:它們沒有顯示出來,並且隱含轉換更寬容 - 表現得有點像simi larly到#define

C++ 11增加了enum class這與C#的enum非常相似。但是,沒有內置的功能類似於Java或Swift的enum的靈活性。

你會想是這樣的:

enum class PlayerState { 
    Stopped, 
    Playing, 
    Paused 
} 

如果Reset不是一個國家,而是一個動詞,那麼它不應該是在enum定義。

class VideoPlayer { 
private: 
    PlayerState state; 
public: 
    VideoPlayer() : 
     state(PlayerState::Stopped) 
    { 

    } 

    void play() { 

     switch(this->state) { 
      case PlayerState::Stopped: 
       // start video here, only change `state` when complete. 
       // you might want to add a "BeginPlaying" (e.g. `LoadingFileFromDisk`) state value too. 
       this->state = PlayerState.Playing; 
       break; 
      case PlayerState::Playing: 
       throw VideoPlayerStateExeception("Already playing"); 
      case PlayerState::Paused: 
       this->resume(); 
       break; 
     } 
    } 


} 
+0

這種方法可以同時處於多個狀態嗎?當你用bools自然地獲得它。 – sabotage3d

+0

@ sabotage3d不,這是故意的。請閱讀關於有限狀態機的鏈接維基百科文章, – Dai

+0

我非常瞭解FSM和HFSM。我最初的問題是處理多個國家。我將修改我的示例以使其更清楚。 – sabotage3d

0

我會用C++ 17的std::variant。數據將成爲各國的類型。

struct Playing { 
    float time_from_start; 
}; 

struct Paused { 
    float time_from_start; 
}; 

struct Stopped {}; 

struct MediaPlayer { 
    std::variant< 
     Playing, 
     Paused, 
     Stopped 
    > state; 

    void play() { 
     state = Playing{}; 
    } 

    void pause() { 
     state.visit([&](auto&& s){ 
      using S = std::decay_t<decltype(s)>; 

      float time = 0; 

      if constexpr (std::is_same_v<S, Playing>) { 
       time = s.time_from_start; 
      } 

      state = Paused{time}; 
     }); 
    } 
}; 

甚至可以添加其他一些有用的狀態:

struct ABLoop { 
    float a, b, time_from_a; 
};