Outils d'utilisateurs

Outils du Site


definir_ses_types

Ceci est une ancienne révision du document !


Créer des nouveaux types

Définir un type

Dans les chapitres précédents, vous avez appris qu'il était possible de modifier des types en ajoutant des modificateurs. Il sera par exemple possible de passer d'un entier sur 32 bits (int) en un entier sur 16 bits en ajoutant le mot-clé short.

Imaginons que l'on écrive le code suivant :

int x {};
int y {};
int z {};
int a {};
int b {};
int c {};

Après analyse des contraintes imposées par les données, il apparaît que les valeurs prises par ces variables seront comprises entre 0 et 100. Vous souhaitez donc changer les types pour utiliser un type entier non signé sur 8 bits unsigned char au lieu d'un type entier signé int. Vous faites donc le remplacement de tous les types des variables :

unsigned int x {};
unsigned int y {};
unsigned int z {};
unsigned int a {};
unsigned int b {};
unsigned int c {};

On voit tout de suite le problème, puisque c'est le même qui a été présenté pour les variables. La modification manuelle de plusieurs lignes de code diminue l'évolutivité du code et donc la qualité logicielle du programme.

Pour corriger ce problème, on va pouvoir utiliser une solution équivalente à celle utilisée pour les variables. On va pouvoir créer un nouveau type, qui sera en fait un type créé à partir d'un type de base et de modificateurs et au quel on donnera un nouveau nom.

La syntaxe est la suivante, en utilisant le mot-clé using :

using local_type = int;
local_type x {};
local_type y {};
local_type z {};
local_type a {};
local_type b {};
local_type c {};

Avec ce code, il ne sera plus nécessaire de changer plusieurs lignes si on souhaite changer le type des variables. Le code a gagné en maintenabilité et en qualité logicielle.

Attention, vous avez déjà rencontré using, pour déclarer l'utilisation d'un espace de noms. Même si on utilise le même mot-clé, il s'agit bien de deux utilisation différentes et donc deux syntaxes différentes. Pour rappel :

using namespace std; // déclaration d'un espace de noms
using std::cout; déclaration d'un fonctionnalités d'un espace de noms
 
using nouveau_type = ancien_type; // déclaration d'un nouveau type

Pour les noms de type, les règles sont les mêmes que pour les noms de variables (caractères alphanumériques et tiret bas et doit commencer par une lettre). De la même façon, il est classique de définir des règles de codage pour préciser comment il faut écrire les noms de type. Par exemple, il sera classique de terminer un nom de type avec “_t” ou “_type” (par exemple avec la bibliothèque standard ou la bibliothèque Boost) ou avec une majuscule (par exemple avec la bibliothèque Qt).

Type et sémantique

Pouvoir définir un type présente une autre utilité. Lorsque l'on utilise un type (par exemple int), ce type a un sens en termes de représentation (int représente un nombre entier positif ou négatif, sur 32 bits), mais il n'a aucun sens en termes de sémantique. C'est à dire qu'ils ne signifient rien pour celui qui lit le code, en dehors du sens donné par le langage.

Par exemple, dans le code suivant, un entier peut correspondre à un nombre de minutes ou à un nombre d'heures, cela ne changerait rien pour le compilateur. Par contre, cela a une importance pour celui qui écrit le code, mais cette information n’apparaît par directement dans le code.

int temps_1 { 12 }; // heure
int temps_2 { 40 }; // minutes

On peut, comme dans le code précédent, indiquer cette information en commentaire, mais rien ne permet de garantir que la sémantique sera respectée. Ainsi, on peut additionner ces deux nombres, sans que cela ne provoque le moindre remord de la part du compilateur. Alors que additionner des heures et des minutes n'a aucun sens pour nous.

int temps_total { temps_1 + temps_2 };

Une première approche pour corriger ce problème pourrait être de renommer explicitement les types, pour leur donner un nom significatif. Par exemple, nous pouvez créer les types heure et minute de la façon suivante :

using heure = int;
using minute = int;
 
heure temps_1 { 12 };
minute temps_2 { 40 };

Cette correction peut sembler mineure, mais elle correspond à un principe important en programmation. Un code est plus souvent lu qu'il n'est écrit. Il ne faut donc pas écrire vos codes pour qu'ils soient les plus faciles à écrire (en général, pour gagner un maximum de temps lors de l'écriture), mais pour qu'ils soient les plus faciles à lire. L'expérience montre que l'on perd beaucoup de temps à comprendre un ancien code pour corriger les erreurs de programmation ou le faire évoluer.

Il faut donc écrire un code en gardant en tête la question “comment je pourrais réutiliser ce code ?” (ré-utilisabilité). Il faut alors respecter les règles suivantes :

  • Présentez correctement vos codes. Passez à la ligne, indentez vos lignes (c'est-à-dire ajoutez des espaces en début de ligne pour que vos codes soient alignés), aérez votre code (utilisez des espaces pour faciliter la lecture) ;
  • Regroupez vos codes en termes de fonctionnalités. Vous verrez pas la suite comment regrouper vos codes dans des fichiers pour créer des fonctions, classes et modules.
  • Donnez un sens aux choses. Donnez des noms de variable et de type qui ont un sens (sémantique).

Donner un nom explicite aux types vise à répondre à ce troisième point. Cependant, cette approche ne sera pas suffisante. Donner un sens aux choses est intéressant, faire que le compilateur comprennent le sens donné et valide leur bonne utilisation, c'est encore mieux. Ainsi, même en créant des types heure et minute, le compilateur ne fait aucune validation (pour lui, ce sont des entiers) et ne posera pas de problèmes à les additionner.

??? temps_total { temps_1 + temps_2 }; // on met quoi comme type ?

Il faudrait au minimum que le compilateur prévienne que l'on essayer d'additionner n'importe quoi. Ou encore mieux, qu'il comprennent que 1 heure = 60 minutes et qu'il fasse l'addition en faisant la conversion, pour donner le résultat correct. La sémantique des types est incomplète en créant de nouveaux types de cette façon.

Mais heureusement, le C++ propose des techniques plus avancées pour proposer une sémantique complète, validé par le compilateur. Vous verrez dans la suite du cours comment créer ce type de sémantique, en utilisant des classes (programmation orientée objet) et la méta-programmation (création de langages spécifiques d'un domaine - DSL).

definir_ses_types.1403393005.txt.gz · Dernière modification: 2014/06/22 01:23 par gbdivers