Langage C++/Structures, unions et champs de bits

Début de la boite de navigation du chapitre
Structures, unions et champs de bits
Icône de la faculté
Chapitre no 12
Leçon : Langage C++
Chap. préc. :Énumérations
Chap. suiv. :Objet
fin de la boite de navigation du chapitre
En raison de limitations techniques, la typographie souhaitable du titre, « Langage C++ : Structures, unions et champs de bits
Langage C++/Structures, unions et champs de bits
 », n'a pu être restituée correctement ci-dessus.

Structures, unions et champs de bits

modifier

Les structures existent en C++ uniquement pour la compatibilité ascendante et ne servent plus qu’à quelques cas spécifique comme les unions, les champs de bits et quelques applications matérielles mais même dans ces circonstances, il est possible de trouver des alternatives plus "Objet".

Le but principal de la structure était de regrouper et de mieux gérer les données qui avaient des affinités communes dans un langage qui ne disposait pas d'autres moyens (hormis l'énumération) pour organiser les données d'un programme.

Usuellement, les structures sont utilisées conjointement avec le mot clé "typedef". Cette écriture permet d’utiliser le casting et l'auto-référencement en est facilité.

En C++ la structure a été remplacée avantageusement par la classe mais nous verrons cela un peu plus tard.

Il existe trois catégories de structures :

  • Les structures simples,
  • Les unions,
  • Les champs de bits.

Les Structures Simples

modifier

En C++ la structure simple a évolué pour suivre un modèle plus "orienté objet", désormais, il est possible et conseillé d'inclure les méthodes qui manipulent les données de la structure dans la structure elle-même. Chose qui était très lourd à faire en C. On pouvait bien créer un pointeur sur méthode mais cela n'était pas aussi trivial que ce que permet le C++.


Où <NomStructure> (optionnel) est le nom interne de la structure, <StructureParente> (optionel) est le nom de la structure parente, <Visibilite> (optionel) déclare la visibilité des membres (parmi private, protected et public, par défaut : public), <TypeChamp1> est le type du premier champ ayant pour nom <NomChamp1>, <TypeChampN> est le type du dernier champ ayant pour nom <NomChampN>, <TypeMethod1> est le type de la première méthode <NomMethod1> ayant pour paramétrés <ParametresMethod1>, <TypeMethodN> est le type de la dernières méthode <NomMethodN> ayant pour paramètres <ParametresMethodN> et <NomTypeStructure> est le type de la structure

Début de l'exemple
Fin de l'exemple


Alternative à la structure

modifier

En fait, je l'ai déjà dit, la structure a été avantageusement remplacée par la classe, déclarée par le mot clé class.

Les Unions

modifier

Les unions sont une forme de typage faiblement typé. Les unions ont été conservées du C pour compatibilité et ne correspondent pas à la philosophie d'un langage orienté objet.

Les unions permettent de créer des espaces mémoire où l’on peut interpréter une même donnée de différentes manières ou de diviser une même donnée en sous ensembles. En fait, une union déclare la disposition de tous ses membres en partant de la même adresse contrairement à la structure qui dispose ses membres les uns à la suite des autres.

Les unions ne sont pas à proprement parler des structures. Utilisées telles quelles, elles ne permettent pas l'héritage mais il existe un moyen de contourner cet obstacle. Il est possible de créer des unions non nommées, cela permet de déléguer la manipulation des membres directement à la portée supérieure. Sachant cela il suffit d'encapsuler une union dans une structure typée pour rendre l'héritage possible. Cela implique cependant qu’il sera impossible de créer un constructeur ou des méthodes non inlinées au niveau de l'union. Pour des raisons de lecture il sera donc préférable de déléguer la gestion des méthodes de l'union à la portée supérieure (soit la structure dans notre cas). Cela exige aussi que tous les attributs de l'union soient déclarés comme public.


Où <NomStructure>(optionnel) est le nom interne de la structure d'accueil, <TypeChamp1> et <TypeChampN> sont les types des champs respectivement <NomChamp1> et <TypeChampN>, <NomTypeStructure> est le nom du type de la structure.

Début de l'exemple
Fin de l'exemple


Début de l'exemple
Fin de l'exemple


Alternative à l'union

modifier

Comme je l'ai dit plus haut, une classe et ses méthodes qui encapsulent les castings sera plus claire et propre que de manipuler la même variable avec plusieurs noms différents.

Champs de bits

modifier

Les champs de bits (ou "Drapeaux" de l'anglais "Flags"), qui ont leur principale application en industrie, sont des structures qui ont la possibilité de regrouper au plus juste, dans un nombre d'octets moindre, plusieurs valeurs. Cela vient directement du monde de l'électronique. Il existe nombres d'exemples de ce fonctionnement dans l’industrie.

Cela dit, les champs de bits sont très fortement déconseillés car aucun standard C, C++ ne fixe les règles d'implémentations.

La norme ISO C++ 14 (ISO/IEC JTC1 SC22 WG21 N 3690) énonce ceci :

"Allocation of bit-fields within a class object is implementation-defined. Alignment of bit-fields is implementation-defined."

Cela signifie qu'aucune garantie n'est fournie en ce qui concerne l'alignement mémoire. certains compilateurs peuvent générer un exécutable demandant l'accès à deux cases mémoire simultanément. Si cela ne pose que des problèmes de performances sur Intel, d'autres processeurs peuvent générer une erreur d'accès non alignée à la mémoire (Cas des processeur Spark par exemple).

Cela signifie également que chaque compilateur peut implémenter le champ de bit à sa manière dans une classe (et donc par extension dans une structure). Deux compilateurs peuvent alors générer deux exécutables aux comportement différents à partir du même code source.

Cela est vrai surtout en cas de cast ou d'union permettant de d'accéder à un champ de bit comme un seul entier ou champ par champ. Dans ce cas, rien ne garantit l'ordre dans lequel seront accédés les champs de bit.

La tentation est trop forte pour ne pas faire une petite parenthèse sur le standard C (ISO/IEC 9899:201x) qui est encore plus explicitement permissif :

"An implementation may allocate any addressable storage unit large enough to hold a bit- field. If enough space remains, a bit-field that immediately follows another bit-field in a structure shall be packed into adjacent bits of the same unit. If insufficient space remains, whether a bit-field that does not fit is put into the next unit or overlaps adjacent units is implementation-defined. The order of allocation of bit-fields within a unit (high-order to low-order or low-order to high-order) is implementation-defined. The alignment of the addressable storage unit is unspecified."

Cela veut dire la même chose mais sans sous entendus.

Bref, voici un champ de bit :

Où <NomChampsBits> est le nom interne des champs de bits(facultatif), <TypeChamp> est le type (ou taille totale) du champs de bit, [<NomChamp1>] et [<NomChampN>] sont les nom des sous-champs(facultatif), <NombresBits1> et <NombresBitsN> sont les taille des sous-champs, respectivement, [<NomChamp1>] et [<NomChampN>], <NomTypeChampsBits> est le nom du type donné au champs de bits

Début de l'exemple
Fin de l'exemple


Alternative aux champs de bits

modifier

À l'instar de l'union et de la structure, une classe et ses méthodes qui encapsulent les castings seront plus claires et propres que de manipuler plusieurs sous-noms différents d'une même variable.