19

私はフラグを使ったプログラミングにあまり慣れていませんが、フラグが役立つ状況を見つけたと思います。

特定のイベントのリスナーとして自分自身を登録するオブジェクトがいくつかあります。それらが登録するイベントは、構築時に送信される変数に依存します。これを行う良い方法は、次のようなビットごとの OR 接続変数を送信することだと思いますTAKES_DAMAGE | GRABBABLE | LIQUID:

しかし、これは私が混乱するところです。できれば、フラグはenum. しかし、それも問題です。これらのフラグを取得した場合:

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

次に、フラグSOME_OTHER(3) を送信することは、 を送信することと同じGRABBABLE | LIQUIDですよね?

このようなものをどのように正確に扱いますか?

4

6 に答える 6

61

列挙は 2 の累乗である必要があります。

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

に設定myVarしたGRABBABLE | TAKES_DAMAGEとします。GRABBABLE フラグを確認する必要がある場合の動作は次のとおりです。

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

に設定myVarしたLIQUID | SOME_OTHER場合、操作の結果は次のようになります。

 myVar:     00001100
 GRABBABLE: 00000010 [AND]
 -------------------
            00000000 // zero => converts to false
于 2009-10-27T14:42:21.533 に答える
31

フラグを格納する別の方法は、基になる型をまったく気にしないことです。列挙型を使用する場合、列挙型の値はデフォルトで unsigned int に格納されます。これは、一般的なコンピューターでは 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();

このメソッドを使用すると、制限なしに任意の数のフラグを定義でき、オーバーフローを心配することなくいつでもフラグ セットを拡張できます。欠点は、フラグのサブセットのテストがより面倒で、より多くのコードが必要になることです。

于 2009-10-27T16:01:43.497 に答える
6

はい。代わりに、列挙メンバーを 2 のべき乗にします。

enum
{
    TAKES_DAMAGE = (1 << 0),
    GRABBABLE = (1 << 1),
    LIQUID = (1 << 2),
    SOME_OTHER = (1 << 3)
};
于 2009-10-27T14:42:47.293 に答える
4

列挙型に値を設定することはできませんか?

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

その後、それらに対してビット単位の OR を実行するだけです。

于 2009-10-27T14:42:46.930 に答える
4

フラグは 2 のべき乗のみにする必要があります。つまり、それぞれがこれを格納するデータ型のビットであり、ビット単位の OR を実行してもオーバーラップするものはありません。

于 2009-10-27T14:42:10.287 に答える
3

あなたが必要

enum
{
    TAKES_DAMAGE = 1,
    GRABBABLE = 2,
    LIQUID = 4,
    SOME_OTHER = 8
};
于 2009-10-27T14:42:31.957 に答える