Ce texte résulte d'un échange avec Yannick Triqueneaux, cohorte 08 du DDJV de l'Université de Sherbrooke.
Pour qui vient d'un autre langage que C++, par exemple Java ou C#, utiliser des enum en C++ peut surprendre à la fois de par les similitudes et les différences avec les pratiques dans ces autres langages. Ce qui suit est un très petit programme (expliqué de façion succincte au fur et à mesure) manipulant un type Jour défini sous la forme d'un enum global; l'idée ici est de vous dépanner, pas de couvrir toutes les possibilités d'un tel type.
Le code présenté ici est du code C++ 03. Avec C++ 11, des raffinements importants sont rendus possibles avec les enum, alors si votre compilateur supporte cette partie du nouveau standard, profitez-en!
De prime abord, un enum en C++ est un type, nommé ou non, qui regroupe des constantes entières connues dès la compilation. À moins que le programme ne dicte explicitement des valeurs pour ces constantes, la valeur de chacune pour un même enum sera distincte de celles des autres (la première vaudra 0, la deuxième vaudra 1, la troisième vaudra 2, etc.). À moins que le programme n'impose une valeur explicitement à une constante énumérée, celle-ci vaudra un de plus que celle qui la précède dans l'ordre de leurs déclarations.
Ainsi, dans le programme suivant, A vaut 0, B vaut 1, C vaut -2, D vaut -1 et E vaut 0 (il est donc possible pour deux symboles d'une même énumération d'avoir la même valeur) :
enum { A, B, C = -2, D, E };
Une constante énumérée est un entier connu à la compilation. À ce titre, elle peut par exemple être utilisée pour déterminer le nombre d'éléments d'un tableau automatique :
enum { N = 10 };
float tab[N] { 0.0f }; // tableau de N float initialisés à zéro
Dans une classe, l'utilisation d'un enum peut surpendre.
Dans l'exemple de la classe Etat, à droite, un type énuméré Etat::Possibilite est défini comme pouvant prendre les valeurs Etat::AVANT, Etat::PENDANT et Etat::APRES. Remarquez que c'est le nom de la classe qui qualifie les noms des symboles, en non pas le nom du type énuméré lui-même. Le programme de test (à droite lui aussi) met ceci en relief, que ce soit pas l'initialisation de l'instance etat du type Etat ou de la variable poss du type Etat::Possibilite. À l'intérieur d'une méthode de la classe Etat, il n'est pas nécessaire de qualifier les symboles énumérés locaux, ces noms étant qualifiés par le nom de la classe (préfixe Etat::) ce qui est alors implicitement le cas. La définition de la méthode Etat::est_avant() en fait la démonstration de par la manière dont elle accède à AVANT. |
|
L'exemple à droite présente le type Jour, une énumération représentant les jours de la semaine. Ce type est global, pour les besoins de l'exemple. Vous remarquerez la classe JourInvalide, représentant (sans grande surprise) un jour invalide, et qui servira à fins de levée d'exceptions. Pour les actions préventives, est_valide(j) sera vrai seulement si j est un Jour valide (entre Dimanche et Samedi inclusivement avec notre implémentation). Qu'un Jour puisse être invalide peut surprendre : comment un Jour peut-il être invalide s'il est représenté par un élément d'un ensemble fini de constantes énumérées? En fait, un enum en C++ est un entier, et les compilateurs ne valident typiquement pas les bornes de validité des entiers utilisés pour les initialiser (simple question de vitesse d'exécution). Pour cette raison, nous devrons déployer quelques efforts en ce sens dans notre implémentation. Les autres services vont de soi :
|
|
L'implémentation des services associés au type Jour va comme suit :
Le passage de Jour à string est automatique en C# ou en Java, mais ne l'est pas en C++, où enum n'est qu'un int. Ceci explique que le passage de l'un à l'autre doive être implémenté manuellement ici. Une implémentation complète demanderait que l'on tienne compte de l'internationalisation, ce que nous ne ferons pas ici, faute d'espace et de temps. |
|
Un programme de test très simple est proposé à droite. Comme vous pouvez le constater, une fois quelques services de base implémentés (en particulier, ceux rélisant les entrées/ sorties sur des flux), utiliser un type énuméré est simple et, faut bien le dire, plutôt naturel. |
|
Ce qui suit découle d'un échange avec Kenzo Lespagnol, étudiant de la cohorte 07 du DDJV.
Peut-on utiliser des énumérations à titre de paramètres pour des templates? Il se trouve que oui. Ce qui suit est un petit exemple tout simple d'une telle combinaison.
Soit les types énumérés voyelles et jours proposés à droite, de même que des opérateurs de projection sur un flux pour chacun d'eux. Notez que le nom est voyelles est... discutable, du moins pour des symboles qui sont essentiellement globaux |
|
Soit le type générique utiliser<T>, qui offre des services pour utiliser (de manière très banale) un T. |
|
Chacun des utiliser<T> offret trois services :
|
|
La fonction afficher<T>(ostream&) utiliser utiliser<T> pour décrire le type T sur le flux passé en paramètre. |
|
|
|
Pour éviter de la redondance, j'ai localisé les liens complémentaires sur les énumérations à l'adresse ../Divers--cplusplus/enumerations_fortes.html#lectures_complementaires.