2009-10-27 217 views
11

我不是很習慣用旗幟編程,但我想我剛剛發現的情況下,他們會是有用的:標誌,枚舉(C)

我有一對夫婦的對象是自己註冊作爲某些事件的聽衆。他們註冊哪些事件取決於構建時發送給他們的變量。我認爲一個很好的方法是發送按位或連接變量,如:TAKES_DAMAGE | GRABBABLE | LIQUID等。然後,在構造函數中,對象可以檢查設置了哪些標誌並將它自己註冊爲偵聽器。

但這是我困惑的地方。優選地,這些標誌將在枚舉中。但這也是一個問題。如果我們有這些標誌:

enum 
{ 
    TAKES_DAMAGE,/* (0) */ 
    GRABBABLE, /* (1) */ 
    LIQUID, /* (2) */ 
    SOME_OTHER /* (3) */ 
}; 

然後發送標誌SOME_OTHER(3)將是相同的發送GRABBABLE | LIQUID,不是嗎?

你究竟如何處理這個東西?

回答

45

你列舉需要成爲兩個大國:

enum 
{ 
    TAKES_DAMAGE = 1, 
    GRABBABLE = 2, 
    LIQUID = 4, 
    SOME_OTHER = 8 
}; 

或者在一個更可讀的方式:

enum 
{ 
    TAKES_DAMAGE = 1 << 0, 
    GRABBABLE = 1 << 1, 
    LIQUID = 1 << 2, 
    SOME_OTHER = 1 << 3 
}; 

爲什麼?因爲你希望能夠用標誌沒有重疊結合,也能夠做到這一點:

if(myVar & GRABBABLE) 
{ 
    // grabbable code 
} 

...如果枚舉值看起來像這裏面的工作原理:

TAKES_DAMAGE: 00000001 
GRABBABLE: 00000010 
LIQUID:  00000100 
SOME_OTHER: 00001000 

所以,說你已經設置myVarGRABBABLE | TAKES_DAMAGE,這裏的工作原理是,當你需要檢查的GRABBABLE標誌:

myVar:  00000011 
GRABBABLE: 00000010 [AND] 
------------------- 
      00000010 // non-zero => converts to true 

如果要設置myVarLIQUID | SOME_OTHER,該操作將導致:

myVar:  00001100 
GRABBABLE: 00000010 [AND] 
------------------- 
      00000000 // zero => converts to false 
+0

@jldupont - 我相信你的意思是「shiFt運營商」? 8v) – 2009-10-27 14:48:12

+0

@jldupont我認爲你錯過了一封信... – Greg 2009-10-27 14:48:28

+0

不要以爲我同時收到5個回覆。 o.o 感謝您的出色解釋和解決方案。 – quano 2009-10-27 14:48:57

4

您應該只對兩個標誌賦予權力,即在您存儲該標誌的任何數據類型中,每個標誌都有一點位,並且在按位OR時沒有任何重疊。

3

你需要

enum 
{ 
    TAKES_DAMAGE = 1, 
    GRABBABLE = 2, 
    LIQUID = 4, 
    SOME_OTHER = 8 
}; 
+0

我認爲你需要修復這一個的語法。 (我知道,同樣的錯誤是在這個問題上,但現在已經被修復了。) – 2009-10-27 15:01:14

+0

@fred larson,修正:-) – Fredou 2009-10-27 15:15:56

4

你就不能設置的值在枚舉?

enum { 
TAKES_DAMAGE = 1, 
GRABBABLE = 2, 
LIQUID  = 4 
} 

之後,只是按比特或對他們進行處理。

5

是的。相反,使你的枚舉成員的權力2:

enum 
{ 
    TAKES_DAMAGE = (1 << 0), 
    GRABBABLE = (1 << 1), 
    LIQUID = (1 << 2), 
    SOME_OTHER = (1 << 3) 
}; 
+0

對於downvoter:或許你想解釋一下你發現對這個答案沒有用處? – 2009-10-27 14:55:06

25

存儲標誌的另一種方式是根本不打擾底層類型。當使用枚舉時,枚舉值默認存儲在一個無符號整數,這是一個普通計算機上的32位。這隻給你32個可能的標誌:雖然肯定很多,但有些情況下它不夠。

現在你可以定義你的標誌設置是這樣的:

typedef struct 
{ 
    int takes_damage : 1; 
    int grabbable : 1; 
    int liquid  : 1; 
    int some_other : 1; 
} flags; 

,如果你從來沒有遇到過這樣的「1」的部分告訴編譯器只使用1位來存儲這些結構成員。

現在你可以定義一個變量來保存標誌和工作與國旗:

flags myflags = {1,0,0,1}; // defines a variable holding a set of flags, with an initial value of takes_damage & some_other 

myflags.liquid = 1; // change the flags to include the liquid 

if (myflags.takes_damage) // test for one flag 
    apply_damage(); 
if (myflags.liquid && myflags.some_other) // test for multiple flags 
    show_strange_behavior(); 

這種方法允許你定義任意數量的標誌,沒有限制,你可以擴展你的標記設置在任何時候都不用擔心溢出。缺點是測試標誌的一個子集比較麻煩並且需要更多的代碼。

+2

有趣。我第一次遇到這種方法。與傳統的兩枚枚舉的力量相比,我會對閱讀更多知識淵博的人的評論感興趣,而不是比我更多地瞭解其優點和缺點。它適用於所有的編譯器和所有體系結構嗎?什麼是陷阱? – gregschlom 2011-11-04 12:09:31

+0

@gregschlom我也會對更多評論感興趣。它完全符合標準,因此它應該可以與任何編譯器一起工作。我能想到的唯一缺陷是:1.如果結構沒有封裝,標誌集可能會佔用大量內存,其中大部分未使用。 2.你不能一次指定多個標誌值,比如'myflags = grabbable&liquid'(但是,這可以通過使用包含struct和int值的聯合來實現,但是這會產生更多的約束,更多的依賴於編譯器並不是這篇文章的主題) – 2011-11-04 12:26:45

+0

謝謝!另一個問題(與您不能測試標誌子集的事實有關,就像您指出的那樣)是因爲您不容易測試兩個標誌變量是否相等。 (即:你不能這樣做:if(myflags == someOtherFlags){...})。對我來說是一個阻擋者,要堅持使用普通的舊枚舉...... – gregschlom 2011-11-10 22:39:42