602 votes

Pourquoi la classe enum est-elle préférée au plain enum?

J'ai entendu quelques personnes qui recommandent d'utiliser enum classes en C++ en raison de leur type de sécurité.

Mais qu'est que cela signifie vraiment?

Je déteste quand les gens disent quelque chose est bon ou quelque chose est mal , sans expliquer exactement pourquoi ils disent ces choses.

Je ne pouvais pas trouver une réponse claire sur StackOverflow, donc je l'ai regardé et aimerais répondre à cette question. J'espère qu'il sauve quelqu'un de débogage d'un jour :) d'Autres réponses et commentaires sont les bienvenus si il n'y a plus à dire.

655voto

Oleksiy Points 3145

C++ a deux sortes d' enum:

  1. enum classes
  2. Plaine enums

Voici quelques exemples de la façon de les déclarer:

 enum class Color { red, green, blue }; // enum class
 enum Animal { dog, cat, bird, human }; // plain enum 

Quelle est la différence entre les deux?

  • enum classes - agent recenseur noms sont locales à l'enum et leurs valeurs ne sont pas convertir implicitement à d'autres types (comme un autre enum ou int)

  • Plaine enums - où énumérateur les noms sont dans le même champ d'application que l'enum et de leur les valeurs de convertir implicitement à des entiers et d'autres types

Exemple:

enum Color { red, green, blue };                    // plain enum 
enum Card { red_card, green_card, yellow_card };    // another plain enum 
enum class Animal { dog, deer, cat, bird, human };  // enum class
enum class Mammal { kangaroo, deer, human };        // another enum class

void fun() {

    // examples of bad use of plain enums:
    Color color = Color::red;
    Card card = Card::green_card;

    int num = color;    // no problem

    if (color == Card::red_card) // no problem (bad)
        cout << "bad" << endl;

    if (card == Color::green)   // no problem (bad)
        cout << "bad" << endl;

    // examples of good use of enum classes (safe)
    Animal a = Animal::deer;
    Mammal m = Mammal::deer;

    int num2 = a;   // error
    if (m == a)         // error (good)
        cout << "bad" << endl;

    if (a == Mammal::deer) // error (good)
        cout << "bad" << endl;

}

Conclusion:

enum classes doivent être préférés car ils causent moins de surprises qui pourraient potentiellement conduire à des bugs.

293voto

PaperBirdMaster Points 2262

De Bjarne Stroustrup du C++11 FAQ:

L' enum classes ("nouvelle enums", "fort enums") attaquer à trois problèmes avec la traditionnelle C++ énumérations:

  • conventionnelle enums convertir implicitement int, à l'origine des erreurs quand quelqu'un ne veut pas d'une énumération d'agir comme un entier.
  • conventionnelle enumà l'exportation de leurs agents recenseurs pour le reste de la portée, provoquant des collisions de noms.
  • le type sous-jacent d'un enum ne peut pas être spécifié, de causer de la confusion, des problèmes de compatibilité, et fait en avant de la déclaration de impossible.

La nouvelle enums sont "enum class", car ils combinent les aspects de la traditionnelle énumérations (noms de valeurs) avec des aspects de classes (dans l'étendue membres et absense de conversions).

Donc, comme mentionné par d'autres utilisateurs, le "fort des enums" permettrait de rendre le code plus sûr.

La seule chose que vous pouvez garantir à propos de "traditionnelles" enum , c'est le fait que l'expression qui définit la valeur d'une constante d'énumération doit être une constante entière suffisamment grand pour contenir toutes les valeurs de l' enum; c'est généralement un int. Aussi chaque type énuméré est compatible avec char ou un signed/unsigned integer.

C'est une grande description de ce qu'est un enum doit être du type, de sorte que chaque compilateur va prendre decissions sur son propre sur le type de la traditionnelle enums et parfois, le résultat peut être surprenant.

Par exemple, j'ai vu du code comme ça un tas de fois:

enum E_MY_FAVOURITE_FRUITS
{
    E_APPLE        = 0x01,
    E_WATERMELON   = 0x02,
    E_COCONUT      = 0x04,
    E_STRAWBERRY   = 0x08,
    E_CHERRY       = 0x10,
    E_PINEAPPLE    = 0x20,
    E_BANANA       = 0x40,
    E_MANGO        = 0x80,
    E_MY_FAVOURITE_FRUITS_FORCE8 = 0xFF // 'Force' 8bits, you're sure?
};

Dans le code ci-dessus, certains naïfs codeur est de penser que le compilateur va stocker l' E_MY_FAVOURITE_FRUITS des valeurs dans un entier non signé 8 bits type... mais il n'y a aucune garantie à ce sujet, en ajoutant le champ E_MY_FAVOURITE_FRUITS_FORCE8 est un fardeau et ne force le compilateur à faire toute sorte de choix sur le type de l' enum.

Si il y a un morceau de code qui s'appuient sur le type la taille et/ou suppose qu' E_MY_FAVOURITE_FRUITS serait d'une certaine largeur (sérialisation des routines vient à mon avis) ce code peut se comporter dans d'étranges façons, selon le compilateur pensées.

Et pour empirer les choses, si certains collègue ajoute négligemment une nouvelle valeur à nos enum:

    E_DEVIL_FRUIT  = 0x100, // New fruit, with value greater than 8bits

Le compilateur ne veut pas s'en plaindre! Il vient de se redimensionne le type pour s'adapter à toutes les valeurs de l' enum (en supposant que le compilateur ont été en utilisant la plus petite possible, ce qui est une hypothèse que nous ne pouvons pas faire). Cette simple et peu plus de la enum pourrait subtilité casser le code associé.

Mais ce problème est géré par l' enum classes en permettant de spécifier le type:

enum class E_MY_FAVOURITE_FRUITS : unsigned char
{
    E_APPLE        = 0x01,
    E_WATERMELON   = 0x02,
    E_COCONUT      = 0x04,
    E_STRAWBERRY   = 0x08,
    E_CHERRY       = 0x10,
    E_PINEAPPLE    = 0x20,
    E_BANANA       = 0x40,
    E_MANGO        = 0x80,
    E_DEVIL_FRUIT  = 0x100, // Warning!: constant value truncated
};

Désormais, il est possible d'assurer un type (signés ou non) et de la largeur, de sorte que si un champ ont une expression hors de la plage de l' enum nous allons être appréciée avec un avertissement (et le compilateur ne change pas le type de l' enum).

Je pense que c'est une bonne amélioration de la sécurité.

59voto

Saksham Points 3010

L'avantage fondamental d'utiliser Enum Class par rapport aux énumérations normales est que vous pouvez avoir les mêmes variables d'enum pour 2 énumérations différentes et que vous pouvez toujours les résoudre (ce qui a été mentionné comme type sûr par OP).

Par exemple:

 enum class Color1 { red, green, blue };    //this will compile
enum class Color2 { red, green, blue };

enum Color1 { red, green, blue };    //this will not compile 
enum Color2 { red, green, blue };
 

En ce qui concerne les énumérations de base, le compilateur ne sera pas en mesure de distinguer si red se réfère au type Color1 ou Color2 comme indiqué ci-dessous.

 enum Color1 { red, green, blue };   
enum Color2 { red, green, blue };
int x = red;    //Compile time error(which red are you refering to??)
 

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X