« Langage C++/Structures, unions et champs de bits » : différence entre les versions
→Les Unions : Les unions sont une forme de typage faiblement typé. Sauf cas particulier, leur utilisation n'est pas conseillée; d'autant plus qu'elles ne correspondent pas à la philosophie d'un langage orienté objet. |
→Structures, unions et champs de bits : Simplification ( suppression de concepts peu clairs et non validés et/ou faux). |
||
Ligne 13 : | Ligne 13 : | ||
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. |
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 est facilité. |
|||
En C++ la structure à été remplacé avantageusement par la classe mais nous verrons cela un peu plus tard. |
En C++ la structure à été remplacé avantageusement par la classe mais nous verrons cela un peu plus tard. |
Version du 6 janvier 2015 à 21:20
Structures, unions et champs de bits
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 circonstance 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 est facilité.
En C++ la structure à été remplacé 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
En C++ la structure simple à évoluée 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++.
typedef struct [<NomStructure>][: <StructureParente>]
{
[[<Visibilite>:] <TypeChamp1> <NomChamp1>;]
[...]
[[<Visibilite>:] <TypeChampN> <NomChampN>;]
[[<Visibilite>:] <TypeMethod1> <NomMethod1>(<ParametresMethod1>);]
[...]
[[<Visibilite>:] <TypeMethodN> <NomMethodN>(<ParametresMethodN>);]
}<NomTypeStructure>;
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
Dans "TestStruct.h"
#ifndef TESTSTRUCT_H
#define TESTSTRUCT_H
typedef struct TestStruct; // Nécessaire uniquement pour l'auto-pointeur.
typedef struct TestStruct // Nom de structure local nécessaire uniquement pour les méthodes non inlinées
{
private:
TestStruct* aSuivant; // auto-pointeur
int aValeurTest; // Valeur de test
public:
TestStruct(); // constructeur par défaut
TestStruct(TestStruct* pSuivant); // Constructeur paramétré
TestStruct(TestStruct& pCopie); // Constructeur par copie
virtual ~TestStruct(); // Destructeur
TestStruct* mSuivant(); // Accesseur en lecture pour aSuivant
int mValeurTest(); // Accesseur en lecture pour aValeurTest
void mValeurTest(int pNouvelleValeur); // Accesseur en écriture pour aValeurTest
}TestStruct; // Nom du type
#endif // TESTSTRUCT_H
Dans "TestStruct.cpp"
#include "TestStruct.h"
TestStruct::TestStruct():aSuivant(0),aValeurTest(0)
{
//ctor
}
TestStruct::TestStruct(TestStruct* pSuivant):aSuivant(pSuivant),aValeurTest(0)
{
//ctor
}
TestStruct::TestStruct(TestStruct& pCopie):aSuivant(pCopie.mSuivant()),aValeurTest(pCopie.mValeurTest())
{
//ctor
}
TestStruct::~TestStruct()
{
//dtor
}
TestStruct* TestStruct::mSuivant()
{
return this->aSuivant; // retour de aSuivant
}
int TestStruct::mValeurTest()
{
return this->aValeurTest; // retour de la valeur enregistrée
}
void TestStruct::mValeurTest(int pNouvelleValeur)
{
if(this->aValeurTest != pNouvelleValeur) // Si la nouvelle valeur est bien différente de celle déjà enregistrée
{
this->aValeurTest = pNouvelleValeur; // Affectation de la nouvelle valeur.
}
}
Dans "main.cpp"
#include "TestStruct.h"
int main(int argc, char* argv[])
{
TestStruct* vA = new TestStruct(); // Construction d'une structure en tas
vA->mValeurTest(9); // Attribution de la valeur 9 dans la variable test de vA
TestStruct vB(vA); // Construction d'une structure en pile et assignation de vA comme suivant
vB.mValeurTest(vB.aSuivant()->mValeurTest() - 1); // Affectation de 8 (= (9 de vA) - 1) dans la variable test de vB
TestStruct vC(vB); // clonage de vB dans vC
cout << "vA->" << vA << ":Suivant=" << vA->mSuivant() << ":ValeurTest=" << vA->mValeurTest();
cout << "vB." << &vB << ":Suivant=" << vB->mSuivant() << ":ValeurTest=" << vB->mValeurTest();
cout << "vC." << &vC << ":Suivant=" << vC->mSuivant() << ":ValeurTest=" << vC->mValeurTest();
delete vA; // Destruction de vA
}
Alternative à la structure
En fait je l'ai déjà dit la structure à été avantageusement remplacée par la classe déclaré par le mot clé class.
Les Unions
Les unions sont une forme de typage faiblement typé. Sauf cas particulier, leur utilisation n'est pas conseillée; d'autant plus qu'elles 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 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és 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é comme public.
Syntaxe:
typedef struct [<NomStructure>]
{
private:
union
{
public:
[<TypeChamp1> <NomChamp1>;]
[...]
[<TypeChampN> <NomChampN>;]
};
[...]
}<NomTypeStructure>;
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.
typedef struct TestUnion
{
private:
union // Ici l'union permet de lire le long ou d'utiliser sa valeur comme adresse d'une chaine.
{ // Ce genre d'union n'est pas très intéressante car il suffit de caster le long en char*
// pour obtenir le même résultat et l'avantage sera la clarté du casting comparé à l'opacité
// de l'union.
public:
long Valeur;
char* Chaine;
};
//....
}TestUnion;
typedef struct TestStruct
{
short PoidFort;
short PoidFaible;
}TestStruct;
typedef struct TestUnion
{
private:
union // Ici l'union permet de lire l'int ou de lire que sa partie haute (TestUnion.Decoupe.PoidFort) ou
{ // sa partie basse(TestUnion.Decoupe.PoidFaible).
// Ce genre d'union est plus intéressante mais il est possible de faire mieux avec des classes
// pour obtenir le même résultat et l'avantage sera la clarté des méthodes de classes encapsulant
// les castings comparé à l'opacité de l'union.
public:
int Valeur;
TestStruct Decoupe;
};
//....
}TestUnion;
Alternative à l'union
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
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'octet moindre plusieurs valeurs. Cela vient directement du monde de l'électronique. Il existe nombres d'exemples de ce fonctionnement dans l'industrie.
typedef struct [<NomChampsBits>]
{
<TypeChamp> [<NomChamp1>] : <NombresBits1>;
[...]
[<TypeChamp> [<NomChampN>] : <NombresBitsN>;]
}<NomTypeChampsBits>;
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
typedef struct
{
unsigned short Jours : 5; // 0-31 soit de 1 à 31
unsigned short Mois : 4; // 0-15 soit de 1 à 12
unsigned short Annee : 7; // 0-127 soit de 0 à 99, (sans les siècles)
}Date;
Alternative aux champs de bits
À l'instar de l'union et de la structure, une classe et ses méthodes qui encapsulent les castings seront plus clairs et propres que de manipuler plusieurs sous-noms différents d'une même variable.