Cours langage C++ les types constants, références, tutoriel & guide de travaux pratiques en pdf.
Types constants
Réquisitoire contre #define
Il est malaisé, en K&R C tout au moins, de définir des constantes. Utiliser des variables en guise de constante n’est pas une solution, puisque par définition, n’importe qui pourra les modifier. Utiliser la macro #define du préprocesseur n’est pas non plus une solution satisfaisante, car le préprocesseur se bornera à remplacer chaque occurence de l’expression suivant #define par l’expression suivante sur la ligne. Cette manière de faire peut conduire à des messages d’erreur très obscurs de la part du compilateur, et à une recherche de faute ardue. Prenons l’exemple suivant :
Soit un groupe de développement dont vous faites partie, et dont vous utilisez les résultats, comme vos collègues utilisent les vôtres d’ailleurs. Vous définissez un module de programmation qui comprend l’instruction suivante :
#include « file1.h »
file1.h est un de vos fichiers en-tête, que vous utilisez pour définir vos propres types. Il contient la définition d’un type que vous utilisez pour réfléter l’état de vos modules de programme. Plutôt que d’utiliser un int directement, vous avez préféré définir un type intermédiaire (louable intention):
typedef int Status;
Vous utilisez un autre fichier, qui lui n’est pas défini par vos soins, mais par un de vos collègues, pour accéder à du code qu’il a écrit et qui satisfait vos besoins.
#include « file2.h »
Votre collègue ignore ce que vous avez défini, et définit de son côté un état (Status) fixe, égal à 10. Il utilise pour ce faire le préprocesseur :
#define Status 10
Soit vous n’avez pas examiné en détail son fichier file2.h, soit il a été modifié depuis que vous l’aviez examiné: cette ligne (ou ses implications) vous a en tous cas échappé. Dans la suite de votre programme, vous avez défini la ligne suivante :
Status *uneFonctionQuelconque(int sonParamètre) { };
En arrivant sur cette ligne, le compilateur va vous signaler une erreur (vraisemblable
ment quelque chose comme « syntax error », le message d’erreur le plus frustrant que puisse fournir un compilateur de langage) sur la déclaration de votre fonction. Lorsque vous allez tenter de découvrir l’erreur, vous n’en découvrirez pas, parceque l’erreur n’est pas dans le code source, mais uniquement dans un code que vous ne regardez jamais et qui normalement n’est pas disponible, à savoir le code généré par le préprocesseur qui a produit, en lieu et place de votre déclaration de fonction :
10 *uneFonctionQuelconque(int sonParamètre) { };
ce qui conduit au message d’erreur :
mainprog.C, line XXX : error : syntax error //HPC++
On remarque que le compilateur signale l’erreur dans le programme principal. La ligne qu’il signale a de grandes chances de se trouver très loin de la cause effective de l’erreur, et cette cause même n’est pas signalée de manière interprétable. Vous pouvez bien sûr demander un listing intermédiaire du code produit avant compilation, mais ces listings sont souvent très longs, et vous préférerez sans doute recourir à votre instinct de programmeur.
Pour localiser cette erreur, vous allez devoir examiner à la main tous les fichiers que vous incluez dans votre code (en admettant que vous ayez immédiatement le réflexe de chercher dans ce sens, ce qui n’est pas forcément évident). Si au lieu de #define, votre collègue avait utilisé une constante, par exemple :
const int Status = 10;
Le compilateur aurait remarqué que Status est redéfini par file2.h, et aurait immédiatement protesté, en indiquant, par exemple (HPC++)
file2.h, line YYY : error, Status declared as identifier and typedef
Ce qui localise l’erreur immédiatement, bien sûr.
Cet example peut vous paraître artificiel. Il ne l’est pas: en fait il est tiré de plusieurs expériences personnelles, qui étaient peut-être un peu moins triviales quand même.
Utilisation de const
L’utilisation de constantes ne se limite pas à ce genre de cas. Il faut utiliser des constantes chaque fois que l’on désire manipuler des données effectivement constantes. Le compilateur vérifie que vous ne modifiez pas la valeur d’une constante et vous signale une éventuelle violation de cette règle, comme en PASCAL. Il en va de même si vous désirez que l’on ne modifie pas le résultat d’une fonction : déclarez son type de retour constant, comme dans :
const int* uneFonctionQuelconque()
{ static int aValue = 10; return &aValue; }
void main(int argc, char *argv[])
{ int *pointeurEntier = uneFonctionQuelconque(); }
CC: « myprog.C », line XX : error : bad initializer type const int* for pointeurEntier (int* expected) // HPC++
Vouloir assigner un pointeur sur une constante à un pointeur sur une valeur non-constante permettrait par la suite de modifier la valeur que l’on désire conserver constante, d’où la réclamation du compilateur. Pour se conformer à la règle de permissivité de C, vous avez néanmoins la possibilité de contourner cette erreur par un casting explicite (coercion de types). Il est possible, dans la ligne ayant causé l’erreur, d’ajouter l’opérateur de casting de C pour obtenir :
int *pointeurEntier = (int*) uneFonctionQuelconque();
Le programme compile dorénavant sans problèmes. Mais la responsabilité d’une erreur potentielle dûe à cette coercion explicite repose désormais entièrement sur le programmeur. A partir du moment où le programmeur recourt à des conversions de type en C ou en C++, il court-circuite tous les mécanismes de contrôle qu’avait défini le langage. C’est le fameux échappatoire, absent de PASCAL, que Brian Kernighan considérait indispensable.