Dois-je utiliser des #define, enum ou const?
Dans un projet C++ je travaille, j'ai un drapeau type de valeur qui peut avoir quatre valeurs. Ces quatre indicateurs peuvent être combinés. Les drapeaux de décrire les enregistrements dans la base de données et peuvent être:
- nouveau record
- enregistrement supprimé
- enregistrement modifié
- enregistrement existant
Maintenant, pour chaque enregistrement, je souhaite garder cet attribut, donc je pourrais utiliser un enum:
enum { xNew, xDeleted, xModified, xExisting }
Cependant, dans d'autres endroits dans le code, j'ai besoin de sélectionner les enregistrements qui doivent être visibles pour l'utilisateur, donc je voudrais être en mesure de passer qu'un seul paramètre, comme:
showRecords(xNew | xDeleted);
Il me semble que j'ai trois appoaches:
#define X_NEW 0x01
#define X_DELETED 0x02
#define X_MODIFIED 0x04
#define X_EXISTING 0x08
ou
typedef enum { xNew = 1, xDeleted, xModified = 4, xExisting = 8 } RecordType;
ou
namespace RecordType {
static const uint8 xNew = 1;
static const uint8 xDeleted = 2;
static const uint8 xModified = 4;
static const uint8 xExisting = 8;
}
Les besoins en espace sont importants (byte vs int) mais pas indispensable. Avec les définit, je perds le type de sécurité et la enum
je perds de l'espace (des entiers) et probablement d'avoir à jeter quand je veux faire une opération au niveau du bit. Avec const
je pense que j'ai aussi perdre de sécurité de type depuis un hasard uint8
pourrait obtenir par erreur.
Est-il un autre moyen le plus propre?
Sinon, que ferais-tu et pourquoi?
P. S. Le reste du code est plutôt moderne et propre de C++ sans #define
s, et j'ai utilisé les espaces de noms et des modèles dans quelques places, donc ceux qui ne sont pas de question non plus.
- "avec enum je perds de l'espace (entiers)". Pas nécessairement. Voir stackoverflow.com/questions/366017/... et stackoverflow.com/questions/1113855/... (et de gcc -fshort-enum. (Je suis en supposant que ces réponses sont encore vrai en C++.)
- Dans le cas où vous êtes incertain au sujet de la compatibilité de C et de C++, j'ai trouvé ce lien très utile, voir par exemple enum david.tribble.com/text/cdiffs.htm#C99-enum-type
- C'est un vieux sujet à haute voix, est-il une raison de ne pas parler de C++11 énumérer les classes de cette situation de problème.
Vous devez vous connecter pour publier un commentaire.
Combiner les stratégies pour réduire les inconvénients d'une approche unique. Je travaille dans les systèmes embarqués, donc la solution suivante est basée sur le fait que les entier et les opérateurs sur les bits sont rapides, peu de mémoire & faible flash d'utilisation.
Place enum dans un espace de noms pour éviter les constantes de polluer l'espace de noms global.
Un enum déclare et définit un temps de compilation vérifié tapé. Utilisez toujours le temps de compilation type de vérification pour vous assurer que les arguments et les variables sont donné le bon type. Il n'est pas nécessaire pour la définition de type en C++.
Créer un autre membre pour un état non valide. Cela peut être utile en tant que code d'erreur; par exemple, lorsque vous voulez retourner à l'état, mais l'opération d'e/S échoue. Il est également utile pour le débogage; l'utiliser dans l'initialisation des listes et des destructeurs de savoir si la valeur de la variable doit être utilisée.
Considérez que vous avez deux objectifs pour ce type. Pour suivre l'état actuel du dossier et de créer un masque pour sélectionner des enregistrements dans certains états. Créer une fonction en ligne pour tester si la valeur de type est valable pour votre objectif, en tant que marqueur de l'état par rapport à un état de masque. Cela permettra de repérer les bugs que l'
typedef
est juste unint
et une valeur telle que0xDEADBEEF
peut être dans votre variable non initialisée par ou mispointed variables.Ajouter un
using
directive, si vous souhaitez utiliser le type souvent.La valeur de vérification de fonctions sont utiles dans affirme pour piéger les mauvaises valeurs dès qu'ils sont utilisés. Le plus vite vous attraper un bug lors de l'exécution, le moins de dégâts qu'il peut faire.
Voici quelques exemples pour mettre tout cela ensemble.
La seule façon de garantir la valeur correcte de la sécurité est d'utiliser une classe dédiée à la surcharge de l'opérateur et qui est laissé comme exercice pour un autre lecteur.
IsValidMask
n'autorise pas l'option aucun (c'est à dire0
)?Oublier le définit
Ils vont polluer votre code.
bitfields?
Ne jamais utiliser. Vous êtes plus préoccupé par la vitesse qu'avec économiser 4 ints. À l'aide de champs de bits est effectivement plus lent que l'accès à n'importe quel autre type.
Source: http://en.wikipedia.org/wiki/Bit_field:
Et si vous avez besoin de plus de raisons pour pas utilisation bitfields, peut-être Raymond Chen saura vous convaincre de son Le Vieux Chose De Nouveau Post: L'analyse coûts-avantages de bitfields pour une collection de booléens à http://blogs.msdn.com/oldnewthing/archive/2008/11/26/9143050.aspx
const int?
De les mettre dans un espace de noms est cool. Si elles sont déclarées dans votre pension du RPC ou du fichier d'en-tête, leur valeur sera insérée. Vous serez en mesure d'utiliser l'interrupteur sur ces valeurs, mais il entraîne une légère augmentation de couplage.
Ah, oui: supprimer le mot-clé static. statique est déconseillée en C++ lorsqu'il est utilisé comme vous le faites, et si uint8 est un bâtiment de type, vous n'aurez pas besoin de le déclarer dans un en-tête inclus par des sources multiples d'un même module. En fin de compte, le code devrait être:
Le problème de cette approche est que votre code ne connaît la valeur de votre constantes, ce qui augmente légèrement le couplage.
enum
Le même que const int, avec un peu plus de la frappe.
Ils sont encore polluer l'espace de noms global, cependant.
En passant... suppression de la définition de type. Vous travaillez en C++. Ceux typedefs d'énumérations et les structures sont de polluer le code plus qu'autre chose.
Le résultat est plutôt:
Comme vous le voyez, votre enum est de polluer l'espace de noms global.
Si vous mettez ce enum dans un espace de noms, vous aurez quelque chose comme:
extern const int ?
Si vous voulez diminuer le couplage (c'est à dire être en mesure de masquer les valeurs des constantes, et donc, modifier comme bon vous semble sans avoir besoin d'une recompilation complète), vous pouvez déclarer les services de renseignements comme extern dans l'en-tête, et comme constante dans le fichier CPP, comme dans l'exemple suivant:
Et:
Vous ne serez pas en mesure d'utiliser l'interrupteur sur ces constantes, bien que. Donc en fin de compte, choisissez votre poison...
:-p
Avez-vous exclu std::bitset? Jeux de drapeaux est ce que c'est. Ne
puis
Parce qu'il y a un tas de surcharge de l'opérateur pour bitset, vous pouvez maintenant faire
Ou quelque chose de très similaire à celle - j'apprécierais toute correction, puisque je n'ai pas testé cette. Vous pouvez aussi consulter les bits par index, mais il est généralement préférable de se définir qu'un ensemble de constantes, et RecordType constantes sont probablement plus utile.
En supposant que vous avez exclu bitset, je vote pour la enum.
Je ne crois pas que le casting de l'enum est un inconvénient sérieux - OK, donc c'est un peu bruyant, et l'affectation d'une valeur à un enum est un comportement indéterminé, donc il est théoriquement possible de se tirer dans le pied sur certains inhabituelle C++ implémentations. Mais si vous ne le faites qu'en cas de nécessité (ce qui est quand il va partir int enum iirc), il est parfaitement normal de code que les gens ont vu avant.
Je suis dubitatif sur tout l'espace du coût de l'enum, trop. uint8 des variables et des paramètres ne sera probablement pas utiliser le moins de la pile que ints, afin de stockage uniquement dans les classes questions. Il y a certains cas où l'emballage de plusieurs octets dans une struct va gagner (dans ce cas, vous pouvez lancer les énumérations dans et hors de uint8 de stockage), mais normalement rembourrage va tuer le bénéfice de toute façon.
Donc l'enum n'a pas les inconvénients par rapport aux autres, et comme un avantage vous donne un peu de type de sécurité (vous ne pouvez pas affecter une valeur entière aléatoire, sans explicitement casting) et propres manières de se référer à tout.
De préférence je voudrais aussi mettre le "= 2" dans l'enum, par la manière. Il n'est pas nécessaire, mais un "principe de moindre étonnement", suggère que tous les 4 définitions doivent se ressemblent.
bitset
pour cela? il se traduit généralement par unelong
(dans mon de la mise en œuvre iirc; oui, de combien de gaspillage) ou similaire type intégral pour chaque élément de toute façon, alors pourquoi ne pas utiliser unobfuscated intégrales? (ou, de nos jours,constexpr
avec zéro de stockage)bitset
classe, d'autres que ce qui semble être un récurrentes de courant sous-jacent dans les environs dans les discussions de "ugh, nous devons couvrir le peu recommandables de bas niveau des racines de la langue"uint8
des variables et des paramètres ne sera probablement pas utiliser le moins de la pile deints
" est erroné. Si vous avez un CPU avec des registres de 8 Bits, unint
besoin d'au moins 2 registres tout enuint8_t
besoins que 1, donc vous aurez besoin de plus d'espace de pile parce que vous êtes plus susceptibles d'être registres (qui est aussi plus lente et peut augmenter la taille du code (selon les instructions)). (Vous avez un type, il convient deuint8_t
pasuint8
)Voici quelques articles sur la const vs macros contre les énumérations:
Constantes Symboliques
L'énumération des Constantes vs Objets Constants
Je pense que vous devriez éviter les macros surtout depuis que vous avez écrit la plupart de votre nouveau code est en C++ moderne.
Si possible ne PAS utiliser les macros. Ils ne sont pas trop admiré quand il s'agit de C++ moderne.
Enums serait plus approprié qu'elles fournissent "un sens à la identificateurs" ainsi que le type de sécurité. Vous pouvez clairement dire "xDeleted" est de "RecordType" et qui représentent "type de document" (wow!) même après des années. Consts nécessiterait des commentaires pour que, aussi ils ont besoin de monter et de descendre dans le code.
Pas nécessairement...
Pas nécessairement - mais vous n'avez pas à être explicite dans les points de stockage...
Vous pouvez créer des opérateurs de prendre la douleur hors de l':
La même chose peut arriver avec n'importe quel de ces mécanismes: la portée et la valeur des contrôles sont normalement orthogonale à la sécurité de type (bien que définies par l'utilisateur-types - c'est à dire vos propres classes - peut faire valoir des "invariants" à propos de leurs données). Avec les énumérations, le compilateur est libre de choisir un plus grand type de l'hôte, les valeurs, et non initialisée, corrompus ou tout simplement miss-ensemble enum variable pourrait encore se retrouver interpretting sa séquence de bits qu'un certain nombre, vous vous attendez à - comparaison de l'inégalité à l'une quelconque de l'énumération des identifiants, toute combinaison d'entre eux, et 0.
Bien, à la fin de l'testés et fiables de style C OU binaire de énumérations fonctionne très bien une fois que vous avez champs de bits et d'exploitants dans l'image. Vous pouvez encore améliorer votre résistance avec quelques fonctions de validation et d'affirmations comme dans mat_geek de réponse; des techniques souvent également applicables à la manipulation de string, int, double, valeurs, etc..
On pourrait dire que c'est plus "propre":
Je suis indifférent: les bits de données pack plus serré, mais le code augmente de manière significative... dépend de la façon dont beaucoup d'objets que vous avez, et la lamdbas - beaux soient - ils, sont toujours messier et plus dur pour obtenir le droit de bit-à-bit Rup.
BTW /- l'argument de la sécurité des threads est assez faible à mon humble avis - les annales comme un arrière-plan de l'examen plutôt que de devenir un dominant la prise de force motrice; le partage d'un mutex à travers le bitfields est plus probable que la pratique, même si pas au courant de leur emballage (mutex sont relativement volumineux de données des membres - je avoir à se soucier vraiment de la performance à envisager d'avoir plusieurs mutex sur les membres d'un objet, et j'aurais l'air assez attentivement avis ils étaient des champs de bits). Tous les sous-mot-taille type pourrait avoir le même problème (par exemple, un
uint8_t
). De toute façon, vous pourriez essayer atomique compare-and-swap opérations de style si vous êtes désespéré pour accroître la concurrence.operator|
jette à un type entier (unsigned int
) avant l'instruction|
. Le reste de laoperator|
sera récursivement appel lui-même et provoquer un débordement de pile. Je suggère:return RecordType( unsigned(lhs) | unsigned(rhs) );
. CheersMême si vous devez utiliser 4 octets pour stocker un enum (je ne suis pas familier avec le C++, je sais que vous pouvez spécifier le type sous-jacent en C#), il est toujours en vaut la peine -- utiliser les énumérations.
Dans cette journée et l'âge de serveurs avec des go de mémoire, des choses comme 4 octets vs 1 octet de la mémoire au niveau de l'application, en général, n'a pas d'importance. Bien sûr, si dans votre situation particulière, l'utilisation de la mémoire est si important que cela (et vous ne pouvez pas obtenir C++ pour utiliser un octet à l'arrière de l'enum), alors vous pouvez envisager l 'static const route.
À la fin de la journée, vous devez vous demander, est ce que ça vaut la maintenance frappé de l'utilisation de 'static const' pour les 3 octets de mémoire des économies pour votre structure de données?
Autre chose à garder à l'esprit -- IIRC, sur x86, les structures de données 4 octets aligné, donc, sauf si vous avez un certain nombre d'octets de la largeur des éléments dans votre "enregistrement" de la structure, il pourrait en fait pas d'importance. Test et assurez-vous avant de vous faire un compromis dans la maintenabilité de la performance/de l'espace.
Si vous voulez que le type de sécurité de classes, avec la commodité de l'énumération de la syntaxe et de la vérification du bit, envisager Coffre-fort Étiquettes en C++. J'ai travaillé avec l'auteur, et il est assez intelligent.
Méfiez-vous, cependant. En fin de compte, ce package utilise des modèles et macros!
Avez-vous réellement besoin de passer autour des valeurs d'indicateur conceptuel tout, ou allez-vous avoir beaucoup de per-code du drapeau? De toute façon, je pense avoir cette classe ou structure de 1 bit bitfields pourrait en fait être plus clair:
Ensuite votre dossier de classe pourrait avoir un struct RecordFlag variable membre, les fonctions peuvent prendre des arguments de type struct RecordFlag, etc. Le compilateur doit emballer les bitfields ensemble, en économisant de l'espace.
Je ne serais probablement pas utiliser un enum pour ce genre de chose où les valeurs peuvent être combinées ensemble, plus généralement, les énumérations sont des états mutuellement exclusifs.
Mais quelle que soit la méthode que vous utilisez, afin de le rendre plus clair que ce sont ces valeurs qui sont les bits qui peuvent être combinés ensemble, utilisez cette syntaxe pour les valeurs réelles à la place:
À l'aide d'un décalage vers la gauche, il permet d'indiquer que chaque valeur est destinée à être un peu unique, il est moins probable que, plus tard, quelqu'un ferait quelque chose de mal comme ajouter une nouvelle valeur et attribuer quelque chose d'une valeur de 9.
Basé sur KISS, une forte cohésion et de couplage faible, poser ces questions -
Il est un grand livre "Grande Échelle En C++ De Logiciels De Conception", ce qui favorise types de base de l'extérieur, si vous pouvez éviter un autre fichier d'en-tête/interface de dépendance, vous devriez essayer d'.
Si vous êtes à l'aide de Qt, vous devriez jeter un oeil pour QFlags.
Le QFlags classe fournit un moyen sûr de stocker OU de combinaisons de valeurs enum.
Je préfère aller avec
Tout simplement parce que:
Pas que j'aime plus-ingénieur du tout, mais parfois, dans ces cas, il peut être intéressant de créer une (petite) classe d'encapsuler cette information.
Si vous créez une classe RecordType alors il peut avoir des fonctions comme:
vide setDeleted();
vide clearDeleted();
bool isDeleted();
etc... (ou quelle que soit la convention de costumes)
Il a pu valider les combinaisons (dans le cas où toutes les combinaisons sont légales, par exemple, si les "nouveaux" et "supprimer" ne pouvaient pas être réglés à la même heure). Si vous venez d'utiliser des masques de bits, etc alors le code qui définit l'état doit valider, une classe peut encapsuler la logique aussi.
La classe peut également vous donner la possibilité de joindre significative de la journalisation des informations pour chaque état, vous pouvez ajouter une fonction pour renvoyer une chaîne de caractères de la représentation de l'état actuel etc (ou utilisez le streaming des opérateurs<<").
Pour tous que si vous êtes inquiet au sujet de stockage vous pouvez toujours avoir la classe seulement un 'char' membre de données, alors seulement prendre une petite quantité de stockage (en supposant qu'il est non virtuel). Bien sûr, en fonction du matériel, etc vous pouvez avoir des problèmes d'alignement.
Vous pourriez avoir le nombre de bit valeurs qui ne sont pas visibles par le reste du monde si ils sont à un anonyme, un espace de noms à l'intérieur de la rpc fichier plutôt que dans le fichier d'en-tête.
Si vous constatez que le code à l'aide de l'enum/#define/bitmask etc a beaucoup de "soutien" code invalide combinaisons, l'exploitation forestière, etc alors encapsulation dans une classe peut être utile d'examiner. Bien sûr, la plupart du temps de simples problèmes sont mieux avec des solutions simples...