2010年1月18日 星期一

enum

  • 以前我們在Effective C++ 有看過, 用enum取代 #define 跟 const 可以避免錯誤。
  • 許多遊戲engine 或包含大量UI的AP會使用enum管理音效, 貼圖等recourse。
  • enum還有個有趣的技巧叫 enum hack, 方便我們針對enum眾多recourse 的物件寫基本的測試程式。
請看以下宅力十足的code





enum vs. const

假設今天有一個 GamePlayer如下

class GamePlayer{
public:

    const static int iMage = 0;
    const static int iPaladin = 1;
    GamePlayer(int classType);
    void Attack();
private:
    GamePlayer();
    GamePlayer(const GamePlayer&);
    GamePlayer& operator = (const GamePlayer&);
    int Talent;    

}; 

GamePlayer::GamePlayer(int ClassTtpe){
    Talent = ClassTtpe; 

}; 

void GamePlayer::Attack(){
    switch(Talent){
        case 0:
            printf("fire ball\n");
            break;
        case 1:
            printf("Hammer of Justice\n");
            break; 

        default:
            //exception
            break; 

}
const 可能在添加新職業時犯以下粗心錯誤
const static int iMage = 1;
const static int iMonk= 1;  // the same !
就容易跑出些鬼打牆不容易debug的問題。

enum type check

另外一種危險的錯誤發生在使用GamePlayer的人
如果今天宣告了 GamePlayer(1000);
或某人寫了一隻測試程式想驗證是否每個職業都正常運作與否

for( i = 0 ; i <  1000 ; i++){
        GamePlayer TESTER(i);
        TESTER.Attack(); 

} 
會導致 GamePlayer Attack的時候進入switch default 發生不可預期的exception
煩惱這問題的時候我們可以改寫成這樣
enum SpecialTypeClass{
        Mage = 0,
        Paladin ,
        War    ,
        Shaman ,
        Thief
}; 
宣告GamePlayer的使用者就會被Compiler提醒必須要寫成
GamePlayer Abow(GamePlayer::SpecialTypeClass::Shaman) ;
如此明確的定義也另外增加了程式的閱讀性
這樣寫的另一個好處是 switch case會更好看, 不用擔心數字的一一mapping

switch(Talent){
        case SpecialTypeClass::Mage:
            printf("fire ball\n");
            break;
        case SpecialTypeClass::Shaman:
            printf("Earth shock\n");
            break;
        case SpecialTypeClass::Thief:
            printf("split knife\n");
            break;
        case SpecialTypeClass::Paladin:
            printf("Hammer of Justice\n");
            break;
        default:
            //exception
            break;
}

enum hack

最後回到測試程式的小技巧
(說穿了不值錢.. = =)
enum SpecialTypeClass{
        Mage = 0,
        Paladin ,
        War    ,
        Shaman ,
        Thief,
        TalentNum
}; 
加一個 Bound…
for( i = 0 ; i <  GamePlayer::TalentNum ; i++){
        GamePlayer TESTER((GamePlayer::SpecialTypeClass)i);
        TESTER.Attack(); 

} 
have fun

2 則留言: