Question

Je ne suis pas très habitué à programmer avec des drapeaux, mais je pense avoir trouvé une situation où ils seraient utiles:

J'ai quelques objets qui s'enregistrent en tant qu'auditeurs de certains événements. Les événements pour lesquels ils s'inscrivent dépendent d'une variable qui leur est envoyée lors de leur construction. Je pense qu'une bonne façon de faire serait d'envoyer des variables binaires OU connectées, comme celles-ci: TAKES_DAMAGE | GRABBABLE | LIQUID, etc. Ensuite, dans le constructeur, l’objet peut vérifier quels indicateurs sont définis et s’enregistrer lui-même comme auditeur pour ceux qui le sont.

Mais c’est là que je me perds. De préférence, les drapeaux seraient dans une énumération. Mais c'est aussi un problème. Si nous avons ces drapeaux:

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

L'envoi de l'indicateur SOME_OTHER (3) sera alors identique à l'envoi de GRABBABLE | LIQUID, n'est-ce pas?

Comment gérez-vous exactement ce genre de choses?

Était-ce utile?

La solution

Votre énumération doit être un pouvoir de deux:

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

Ou d'une manière plus lisible:

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

Pourquoi? Parce que vous voulez pouvoir combiner des drapeaux sans chevauchement, vous devez également pouvoir le faire:

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

... Ce qui fonctionne si les valeurs d'énumération ressemblent à ceci:

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

Supposons donc que vous définissiez myVar sur GRABBABLE | TAKES_DAMAGE , voici comment cela fonctionne lorsque vous devez rechercher l'indicateur GRABBABLE:

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

Si vous souhaitez définir myVar sur LIQUID | SOME_OTHER , l'opération aurait abouti à:

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

Autres conseils

Une autre façon de stocker des drapeaux est de ne pas se soucier du tout du type sous-jacent. lors de l'utilisation d'un enum, les valeurs enum sont stockées par défaut dans un entier non signé, qui est de 32 bits sur un ordinateur commun. cela ne vous donne que 32 drapeaux possibles: certes beaucoup, mais il y a des cas où cela ne suffit pas.

vous pouvez maintenant définir votre drapeau comme suit:

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

si vous ne l'avez jamais rencontré, la partie ': 1' indique au compilateur de n'utiliser qu'un bit pour stocker ce membre de structure.

maintenant vous pouvez définir une variable pour contenir les drapeaux et travailler avec ces drapeaux:

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();

cette méthode vous permet de définir un nombre quelconque d’indicateurs, sans limitation, et vous pouvez étendre votre ensemble d’indicateurs à tout moment sans craindre un débordement. L'inconvénient est que le test d'un sous-ensemble des drapeaux est plus lourd et nécessite plus de code.

Oui. Attribuez plutôt à vos membres de l’énumération des pouvoirs de 2:

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

Vous ne devez attribuer aux drapeaux que des puissances égales à deux, c’est-à-dire qu’il s’agit d’un type de données, quel que soit le type de données dans lequel vous stockez cette information, et que rien ne se chevauche lorsque vous utilisez OU au niveau du bit.

Ne pouvez-vous pas simplement définir les valeurs dans l'énumération?

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

Ensuite, effectuez simplement une opération binaire OU sur eux.

vous avez besoin

enum
{
    TAKES_DAMAGE = 1,
    GRABBABLE = 2,
    LIQUID = 4,
    SOME_OTHER = 8
};
Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top