Vous êtes sur la page 1sur 83

C++ coding guide

Guide de dveloppement logiciel C++

Timothe Royer Version : 1997/02/25.

Guide de dveloppement logiciel en C++

C++ coding guide

Rsum
La forme du code La structuration dun projet Lalgorithmique

Abstract

Timothe Royer (royer@uranus.crosat.fr)

version : 1997-02-25

page 2

Guide de dveloppement logiciel en C++

C++ coding guide

Sommaire
RSUM ........................................................................................................................................................ 2 ABSTRACT.................................................................................................................................................... 2 SOMMAIRE................................................................................................................................................... 3 CHAPITRE 1 - PRSENTATION DU GUIDE - PREFACE ....................................................................... 5 INTRODUCTION ............................................................................................................................................. 5 APPLICATION ................................................................................................................................................ 6 CONVENTIONS DE PRSENTATION DU GUIDE ................................................................................................... 7 CHAPITRE 2 - MISE EN FORME DU CODE SOURCE............................................................................ 8 MISE EN PAGE DU CODE SOURCE..................................................................................................................... 8 Indentation.............................................................................................................................................. 8 Prsentation des accolades ..................................................................................................................... 9 Disposition de chaque ligne ...................................................................................................................11 STRUCTURE DES FICHIERS SOURCES...............................................................................................................12 Rpartition du code entre les fichiers .....................................................................................................12 Les inclusions de fichier dentte (headers) ............................................................................................13 Les enttes des fichiers source................................................................................................................14 Le fichier dentte ( .hh ) ....................................................................................................................15 Le fichier de dfinition ( .cc ) .............................................................................................................16 PRSENTATION DINSTRUCTIONS ..................................................................................................................17 NOMMAGE ...................................................................................................................................................18 Majuscules et minuscules .......................................................................................................................18 Choix des identificateurs........................................................................................................................19 LES COMMENTAIRES.....................................................................................................................................20 CHAPITRE 3 - STRUCTURATION LOGIQUE DU PROGRAMME .......................................................22 LES VARIABLES ............................................................................................................................................22 Recommandations communes tous les types.........................................................................................22 Les types prdfinis................................................................................................................................23 Les types utilisateurs simples..................................................................................................................24 LES STRUCTURES DE CONTRLE ....................................................................................................................25 LA CLASSE ...................................................................................................................................................26 LES FONCTIONS ET LES MTHODES ................................................................................................................32 Recommandations communes aux fonctions et aux mthodes..................................................................32 Fonctions ...............................................................................................................................................34 Mthodes................................................................................................................................................35 INCLUSIONS MUTUELLES...............................................................................................................................36 LE PRPROCESSEUR ......................................................................................................................................37 DEBUG ........................................................................................................................................................40 INSTRUCTION...............................................................................................................................................43 CHAPITRE 4 - ALGORITHMIQUE...........................................................................................................45 GESTION DUN SOURCE MAINTENU POUR PLUSIEURS PLATEFORMES. ................................................................47 POINTEURS ..................................................................................................................................................50 STRUCTURES DE CONTRLE ..........................................................................................................................50

Timothe Royer (royer@uranus.crosat.fr)

version : 1997-02-25

page 3

Guide de dveloppement logiciel en C++

C++ coding guide

?. OPTIMISATION ........................................................................................................................................52 ?. DVELOPPEMENT MULTI-PLATEFORMES....................................................................................................53 CHAPITRE 5 - MISE EN UVRE..............................................................................................................54 COMPILATION ..............................................................................................................................................54 BIBLIOGRAPHIE ........................................................................................................................................56 ANNEXE A - APPLICATION DU GUIDE AU CODE C ............................................................................58 ANNEXE B - COMMENT LIER DES MODULES C ET C++ AU SEIN D'UN MME EXCUTABLE60 ANNEXE C - MODLE DE FICHIERS SOURCES ...................................................................................66 ANNEXE D - EXEMPLE DE CLASSE : UN NOEUD DE LISTE DOUBLEMENT CHANE ..............71 ANNEXE E - EXEMPLE DE SCURISATION : LA CLASSE COOKIE.................................................76 ANNEXE F - LEXIQUE ...............................................................................................................................79 ANNEXE G - HISTORIQUE........................................................................................................................80

Timothe Royer (royer@uranus.crosat.fr)

version : 1997-02-25

page 4

Guide de dveloppement logiciel en C++

C++ coding guide

Chapitre 1 - Prsentation du guide - Preface


It said : 'The history of every major Galactic civilization tends to pass through three distinct and recognizable phases, those of Survival, Inquiry and Sophistication, otherwise known as the How, Why and Where phases.' The Hitch Hiker's Guide to the Galaxy Douglas Adams

Introduction
La mise au point dun programme informatique est un domaine mal matris o les dpassements de temps de mise au point et de budget sont presque systmatiques. Cest ce problme que nous souhaitons nous attaquer au travers de ce document. Le dveloppement logiciel au sens strict se dcoupe en trois tapes : analyse, conception et dveloppement. Les mthodes de mise en oeuvre des deux premires tapes sont bien tablies. Ce nest pas le cas du codage : les recommandations de dveloppement logiciel sont rares et peu utilises dans lindustrie. Nous souhaitons combler cette lacune. Elle peut tre due : lanxit des informaticiens de voir leur crativit inhibe ; la difficult de consigner mthodiquement des erreurs et des propositions de solution gnrales (ce document est destin tre amlior par ses lecteurs, merci de vos retours dinformation !) ; Au fait qutre reconnu pour son savoir faire technique peut tre incompatible avec une volution de carrire optimale. Dans cette optique, il prfrable de faire valoir ses capacits de synthse en crivant une mthode danalyse. Cest en tenant compte de ces raisons que nous avons tabli ce document. Il sadresse aux dveloppeurs : Qui ont dj pratiquer la programmation, mais souhaitent amliorer la qualit de leur travail du point de vue de limplmentation (maintenance, rutilisabilit, portabilit...). Qui doivent travailler plusieurs sur un mme code. Dans cette situation, la formalisation du code devient dterminante : les cots de maintenance dpassent les cots de dveloppements. Nous avons consign des rgles : A posteriori, aprs avoir dcouvert un bug et trouv une mthode gnrale qui aurait permis dviter la difficult. Il faut pas simplement se promettre dtre plus intelligent la prochaine fois. En constatant que les dveloppeurs experts se sont constitus un ensemble de rgles. Elles se ressemblent en partie dun dveloppeur lautre. Ces rgles ne peuvent se dduire de la lecture dun manuel de rfrence dun langage. Unanimement, le langage C est considr comme puissant mais difficile mettre en oeuvre. En tant que surensemble du C, le langage C++ est la fois plus puissant et plus difficile employer, par les fonctionnalits supplmentaires quil apporte. Timothe Royer (royer@uranus.crosat.fr) version : 1997-02-25 page 5

Guide de dveloppement logiciel en C++

C++ coding guide

En tenant compte de ltat de lart du langage. Celui-ci a beaucoup volu et le dveloppement industriel nen est que plus difficile. Ce document propose un ensemble de recommandations de codage souples et gnrales pour augmenter la qualit du code source. Il est en particulier destin aux programmes crits en C++. Une annexe propose une application de ce guide la programmation en C. Cependant, une partie des argumentations doit tre suffisamment gnrale pour sappliquer dautres langages : une implmentation objet souple, maintenable et rutilisable correspond un style et non un langage. Selon Dijkstra [Dijkstra 1972] : "As a slow witted human being I have a very small head and I had better learn to leave with it and to respect my limitations and give them full credit, rather than to try to ignore them, for the latter vain effort will be punished by failure". Le dveloppement logiciel est un exercice intellectuel difficile. La taiile et la complexit des projets peut crotre plus rapidement que les capacits intellectuelles des dveloppeurs. Il faut rationnaliser limplmentation. Nous vous proposons un outil pour en dbroussailler la complexit. FIXME Le cerveau humain peut travailler avec un nombre limit dides simultanes. Ce nombre est fix sept plus ou moins deux [Meyer ? ?]. Pourtant, cest avec ces capacits intellectuelles limites que des systmes logiques normes doivent tre conus, implments et maintenus. En particulier, les fonctionalits du C++ remplissent largement les besoins techniques ncessaires llaboration dun logiciel. Les difficults de mise en oeuvre arrivent avec limplmentation, lorsque lensemble des capacits du langage ne se reprsente pas par un modle grable par lhumain.

Application
Seule un guide appliqu est intressant. Il faut plutt considrer ce papier comme une base de travail quil faudra modifier, simplifier ou enrichir en fonction des ides qui viendront lusage. La ralit de la programmation ne tient pas dans un cadre complet et rigide. Cependant, il y a beaucoup damliorations applicables systmatiquement un code source et elles ne sont pas toutes lmentaires. Il ne faudrait pas se priver de les inclure dans le guide cause de quelques exceptions. Chacune des propositions est donc associe lun de ces trois niveaux dexigence :
*** IMPRATIF ***

Ces rgles sont indiscutablement communes toutes les implmentations rigoureuses et efficaces.
*** RECOMMANDATION ***

Recommande. La rgle dcrit comment rsoudre une difficult de manire systmatique. Si la difficult est bien matrise par les programmeurs ils peuvent logiquement continuer dappliquer leur ancienne mthode. Il faut simplement s'assurer que cette autre technique est bien applicable de manire systmatique. Il suffit de prciser en une phrase la raison du choix. Une modification du guide peut tre envisage.
*** AMLIORATION ***

Propose. Les rgles qui semblent difficiles dcrire ou quantifier prcisment, quelle que soit la raison, ne sont que proposes comme modle de codage. De mme, certaines amliorations Timothe Royer (royer@uranus.crosat.fr) version : 1997-02-25 page 6

Guide de dveloppement logiciel en C++

C++ coding guide

sophistiques des recommandations ne sont proposes dans cette catgorie qu titre optionnel. La seule prtention de ce document est daider lcriture du code, une fois lanalyse et la conception acheves. Un logiciel mal conu tiendra difficilement dans un guide de programmation utile. Lorsque la conception ou l'implmentation se passent mal, il faut reprendre lanalyse. Le cot engendr sera toujours infrieur celui de la maintenance d'un module mal crit.

Conventions de prsentation du guide


Les chapitres sont prsents dans un ordre dabstraction croissant. Ils sont dcoups logiquement en paragraphes regroupant quelques rgles concernant un mme domaine. Chacun regroupe quelques rgles traitant dun sujet prcis. Dune manire gnrale, la prcision des exigences va dans un ordre croissant entre les chapitres et au sein des paragraphes. Une recommandation peut se dcomposer en sept parties. Seules les deux premires sont toujours prsentes. Le degr de ncessit (impratif, recommandation, amlioration) et le numro de la rgle ; Lnonc de la rgle; (Pourquoi ?) La justification de la rgle; (Rappel) Un rappel sur un point technique prcis, utile pour comprendre la recommandation; (Comment ?) Explique comment faire pour respecter la rgle, lorsque ce nest pas vident; (Exception) Les exceptions la rgle; (Exemple) Un exemple illustrant la rgle. Attention. Les exemples illustrant les recommandations de ce document sont volontairement concis. Ils peuvent ne pas respecter compltement certaines recommandations de mise en forme du source, en particulier celles conues pour des projets importants (commentaires standards, corps des mthodes en dehors des dclarations...). Certaines notions de programmation matriser sont dcrites dans le lexique mis en annexe de ce document. Il est destin tre enrichi en fonction des besoins des utilisateurs de ce guide.

Timothe Royer (royer@uranus.crosat.fr)

version : 1997-02-25

page 7

Guide de dveloppement logiciel en C++

C++ coding guide

Chapitre 2 - Mise en forme du code source


A program should be light and agile, its subroutines connected like a string of pearls. The spirit and intent of the program should be retained throughout. Their should be neither to o little nor too much, neither needless loops nor useless variables, neither lack of structure nor overwhelming rigidity. A program should follow the "Law of Least Astonishment". What is that law ? It is simply that the program should always respond to the user in a way that astonishes him least. A program, no matter how complex, should act as a single unit. The program should be directed by the logic within rather than by outward appearances. If the program fails in these requirements, it will be in a state of disorder and confusion. The only way to correct it is to rewrite the program. The Tao of programming.

Dans ce chapitre, nous allons prsenter laspect "bas niveau" de ce guide : la disposition du texte du code source dans un fichier. Aucune de ces recommandations na de consquence technique directe, pour le compilateur, par exemple.

Mise en page du code source


Dans les paragraphes qui vont suivre, nous allons prsenter des recommandations de disposition des lignes de code dans un fichier. Indentation
*** IMPRATIF ***

Le code doit tre entirement indent de manire cohrente. (Comment?) Le dcalage vers la droite d'une ligne de code source doit tre proportionnel son niveau d'imbrication logique. (Exemple) Voici une premire possibilit : FIXME // info gcc [Code qual 1994] En voici une autre : En revanche, ce code source est mal indent :
*** RECOMMANDATION ***

La largeur dune indentation peut tre de 4 ou de 8 espaces. Utiliser si possible un caractre tabulation pour indenter. (Exemple) Une mthode pratique consiste redfinir la largeur dune tabulation 4 espaces. Tous les diteurs de texte contemporains le permettent. Excuter (setq tab-width 4) sous emacs ou :set shiftwidth=4 sous vi. Timothe Royer (royer@uranus.crosat.fr) version : 1997-02-25 page 8

Guide de dveloppement logiciel en C++

C++ coding guide

(Comment ?) Penser remplacer chaque tabulation par le nombre d'espaces voulus avant de changer d'diteur pour conserver la prsentation.
*** RECOMMANDATION ***

Utiliser un indenteur automatique de code source dont les paramtres sont fixs pour le projet. (Comment ?) Par exemple, emacs permet en standard dindenter un code source C++, de manire paramtrable. Prsentation des accolades Une paire daccolades dlimite un lment de structure.
*** IMPRATIF ***

Une accolade fermante se trouve toujours la verticale de laccolade ouvrante correspondante. (Exemple) A ne pas faire :
int main(void) { cout << "Les codes ASCII :" << endl; int col; for(int ligne = 0; ligne < 32; ligne++) { for(col = 0; col < 7; col++) { cout << (col*7+ligne) << ' ' << char(col*7+ligne) << '\t'; } cout << endl; } }

Cette disposition permet de gagner deux lignes de code source. Elle fait cependant moins ressortir la structure logique du code. Elle n'est donc pas souhaitable. Le code source doit plutt tre prsent comme ceci :
int main(void) { cout << "Les codes ASCII :" << endl; int col; for(int ligne = 0; ligne < 32; ligne++) { for(col = 0; col < 7; col++) { cout << (col*7+ligne) << ' ' << char(col*7+li gne) << '\t'; } cout << endl; } }
*** IMPRATIF ***

La colonne dans laquelle se trouve une accolade est proportionnelle son niveau dimbrication.
*** IMPRATIF ***

Une structure de contrle utilise toujours une paire daccolades, mme si elle est vide.

Timothe Royer (royer@uranus.crosat.fr)

version : 1997-02-25

page 9

Guide de dveloppement logiciel en C++

C++ coding guide

(Pourquoi?) Lorsque le corps d'une structure de contrle ne contient qu'une expression, les accolades qui l'entourent peuvent tre ommises. Ce n'est pourtant pas souhaitable. La prsence systmatique d'accolades facilite la lecture directe et la maintenance du code. (Exmple) Voici ce qu'il ne faut pas faire :
int main(void) { cout << "Les codes ASCII :" << endl; int col; for(int ligne = 0; ligne < 32; ligne++) { for(col = 0; col < 7; col++) cout << (col*7+ligne) << ' ' << char(col*7+ligne) << '\t'; cout << endl; } }

Se rfrer l'exemple prcdent pour une bonne mthode appliquer.

*** IMPRATIF ***

Pas de caractre point virgule (;) aprs une accolade fermante dlimitant une structure de contrle. (Rappel) Le point virgule n'est requis aprs une accolade fermante que lors d'une dclaration de struct, classe ou template. (Pourquoi?) Des virgules et des points virgules peuvent tre ajouts un peu partout dans un code source sans modifier son excution. Sa lisibilit est cependant altre. Il faut prendre l'habitude de restreindre le syntaxiquement superflu. (Exemple) Cette ligne de code est valide en c et en c++. Elle peut tre ajoute presque n'importe o. Elle gne seulement la lecture.
;;;;;;,,,,,,;,;,;,;,;
*** RECOMMANDATION ***

Une accolade et son contenu commencent sur la mme colonne. (Pourquoi ?) Lors de la production de code pour un gros projet, les rgles de prsentation doivent tre unifies, systmatiques et simples. (FIXME) (Exemple) Voici un programme compilable. Sa prsentation respecte les conventions proposes ici, mais il faudrait ajouter des commentaires au dbut du fichier pour quil soit complet.
#include <iostream.h> const char TAB = '\t'; int main(int argc, char** argv, char** env) { cout << "Les arguments de la ligne de commande :" << endl; for(int argumentCounter = 0; argumentCounter < argc; argumentCounter++) { cout << TAB << argv[argumentCounter] << endl; } cout << endl; cout << "Les variables d'environement :" << endl;

Timothe Royer (royer@uranus.crosat.fr)

version : 1997-02-25

page 10

Guide de dveloppement logiciel en C++

C++ coding guide

char** environementVariableScanner = env; while(environementVariableScanner) { cout << TAB << *environementVariableScanner << endl; environementVariableScanner++; } return 0; }

Disposition de chaque ligne


*** IMPRATIF ***

Dclarer chaque variable sparment et non pas les unes la suite des autres, spares par des virgules. (Pourquoi ?) Dclarer plusieurs variables simultanment est moins lisible et plus difficilement maintenable. Sans le souci de compatibilit avec le C, Bjarne Stroustrup naurait pas implment cette possibilit en C++ [Stroustrup 1995]. (Exemple) Exemple de ce quil ne faut pas faire et de ce quil faut faire :
// <:0 char* papaOurs,mamanOurs,boucleDOr = (char*)0; // d8) char* papaOurs; char* mamanOurs; char* boucleDOr = (char*)0;

Il y avait un pige dans cet exemple : ici, papaOurs est de type char* alors que mamanOurs et boucleDOr sont de type char et non pas de type char*. Bien sr, ce genre de problme a une forte chance dtre dtect par le compilateur lors de lutilisation de la variable. Cependant, cet exemple reste reprsentatif du manque de lisibilit de ce type de dclarations.
*** RECOMMANDATION ***

Le source ne doit pas contenir deux espaces conscutifs (hormis lindentation), ni despace ou de tabulation la fin dune ligne. (Pourquoi ?) Ceci est trs pratique en particulier pour effectuer des remplacements de texte automatiques. Ceux-ci sont trs utiles pour une squence de mots, et la prsentation doit alors tre rigoureuse. Les remplacements, parfois ncessaires, peuvent devenir dramatiques si le code nest pas bien prsent : les erreurs gnres automatiquement ne sont pas toujours faciles dtecter et souvent laborieuses corriger.
*** RECOMMANDATION ***

La largeur dune ligne ne doit pas dpasser 80 caractres. (Pourquoi ?) Ceci permet dditer et dimprimer de manire cohrente un code source sur diffrents supports. (Comment ?) Lorsquune ligne est trop longue, il faut la couper juste avant un oprateur qui sera dans le niveau de parenthsage le plus externe possible. La suite de linstruction sera mis sur une ou plusieurs lignes, toutes indentes une seule fois par rapport au dbut de linstruction.

Timothe Royer (royer@uranus.crosat.fr)

version : 1997-02-25

page 11

Guide de dveloppement logiciel en C++

C++ coding guide

Structure des fichiers sources


Dans cette partie de chapitre, nous allons voir comment le code source se rpartit entre les diffrents fichiers dun programme. Rpartition du code entre les fichiers
*** RECOMMANDATION ***

Les fichiers sources contenant les dclarations ont pour extension .hh et les fichiers sources contenant les dfinitions ont pour extension .cc. (Exception) Certains compilateurs ne connaissent pas ces extensions standard. Il est alors possible dutiliser .h pour le fichier de dclarations et .C, .cxx ou .cpp comme extension pour le fichier de dfinition. En revanche, il ne faut pas utiliser ni lextension .c, ni aucune autre extension dj rserve pour maintenir un programme en C++.
*** RECOMMANDATION ***

Chaque classe est dfinie par deux fichiers sources dont les noms sont le nom de la classe suivi dun .hh ou dun .cc . Le premier contient les dclarations et le deuxime, les dfinitions. (Exemple) Une classe "Spool" sera dfinie par un fichier Spool.hh et un fichier Spool.cc . (Exception) Certaines classes simples peuvent tre maintenues avec la classe qui lutilise. En particulier, le code source dcrivant les classes dont les instances ne sont accdes que par un pointeur ou une rfrence peut tre inclus dans le fichier de la classe qui lutilise. (Rappel) Ce dcoupage de classes en fichiers est obligatoire pour Java, qui est une sorte de C++ interprt plus rcent et plus propre.
*** RECOMMANDATION ***

Les diffrents fichiers dcrivant une classe se trouvent dans un rpertoire qui porte le nom de la classe. (Exemple) Soit une classe ScreenDriver, destine grer un cran. Les fichiers ScreenDriver.hh et ScreenDriver.cc qui la dcrivent se trouvent dans un rpertoire ScreenDriver. Dans ce rpertoire pourront aussi se trouver un Makefile (sous unix) et un fichier ScreenDriverTester.cc qui teste la classe ScreenDriver pour sa scurit et qui en donne des exemples d'utilisation.
*** RECOMMANDATION ***

Le corps des fonctions et des mthodes inline doit se trouver dans les fichiers .hh . (Pourquoi ?) Le corps dune fonction doit tre directement disponible pour le compilateur lors de son insertion dans le corps de la fonction appelante.
*** AMLIORATION ***

Le code source dun module peut tre rparti en trois fichiers au lieu de deux : les fonctions inline peuvent tre maintenues part dans un fichier ayant lextention XXX.icc . Le fichier XXX.cc inclus alors son XXX.hh et son XXX.icc . Ceux-ci ne sincluent pas mutuellement Les autres fichiers peuvent inclure le XXX.hh et ventuellement et XXX.icc . Timothe Royer (royer@uranus.crosat.fr) version : 1997-02-25 page 12

Guide de dveloppement logiciel en C++

C++ coding guide

(Pourquoi ?) Ceci permet de rsoudre clairement les inclusions mutuelles de deux .o contenant chacun du code inline provenant de lautre fichier.
*** AMLIORATION ***

De chaque classe peut dpendre un troisime fichier : XXXTester.cc qui teste la classe et donne un exemple de son interface. (Exemple) Limplmentation de la classe Spool est contenue dans trois fichiers : Spool.hh , Spool.cc et SpoolTester.cc . Les inclusions de fichier dentte (headers)
*** IMPRATIF ***

Ne pas inclure un fichier dentte simplement parce quun autre module ou fichier dentte en a besoin. Ninclure dans un fichier que les enttes directement ncessaires. (Pourquoi ?) Un fichier ne doit pas tre inclus inutilement : cela gne la comprhension, augmente le temps de compilation, le nombre de fichiers recompils et pollue lespace de nommage. (Comment ?) Cette mme technique doit tre utilise pour inclure les headers standards comme les headers utilisateurs. (Exemple) Voici comment inclure les fichiers dentte :
// String.hh #include <iostream.h> // <- La mthode SelfDisplay reoit une // ins tance de ostream. Or cette classe est // dfinie dans iostream.h qui doit donc tre // inclus. class String { char* Data; long Size; // ... String(const char* const); void SelfDisplay(ostream& _targetStream); };

// String.cc #include "String.hh" #include <string.h> // <- Aucune rfrence ce package // standard de gestion de chanes de // caractres n'tait faite dans le fichier // String.hh. En revanche, le fichier // String.cc inclus le header pour // pouvoir utiliser le fonc tion str*. // ... String::String(const char* const _clone) { Size = strlen(_clone) + 1; Data = new char[Size]; strcpy(Data,_clone); }

Timothe Royer (royer@uranus.crosat.fr)

version : 1997-02-25

page 13

Guide de dveloppement logiciel en C++


*** IMPRATIF ***

C++ coding guide

#inclure dans le fichier dentte (.hh) seulement les fichiers dentte ncessaires ce header. Et dans le fichier de dfinition (.cc) correspondant, ce header, ainsi que les autres fichiers dentte .hh utiles au .cc. (Pourquoi ?) Les autres modules qui incluent le .hh nincluront que les fichiers denttes ncessaires ce .hh, mais pas au code associ.
*** RECOMMANDATION ***

Pour viter toute ambigut, inclure les fichiers dentte non standards en prcisant leur rpertoire pre lorsqu'il s'agit de classes utilisateur. (Exemple)
#include <String/String.hh>
*** AMLIORATION ***

Pour amliorer la vitesse de compilation, utiliser un mcanisme dinclusion conditionnel de fichiers denttes. (Exemple) FIXME Mesure quantitative
#if !defined(String_hh) #include <String/String.hh> #endif // !defined(String_hh)

Les enttes des fichiers source


*** RECOMMANDATION ***

Tout fichier source commence par un commentaire indiquant les informations suivantes :
/////////////////////////////////////////////////////////////// // // File name : CodeQuality.txt // // Creation : 1995/07/07 // // Version : 1995/07/11 // // Author : Timothy Royer // // email : tim@puff.frmug.fr.net // // Purpose : Provide an efficient coding standard. // // Distribution : Unlimited (Copyright?) // // Use : Read this description. Try it. Improve it. // ///////////////////////////////////////////////////////////////

Le champ Distribution indique dans quelles conditions le fichier source peut tre distribu. Le champ Use indique comment utiliser/compiler le fichier. De plus, le .cc contient ces informations qui sont destines voluer :
// // Todo : // // O Eliminer les fautes d'orthographe // / Ajouter des exemples

Timothe Royer (royer@uranus.crosat.fr)

version : 1997-02-25

page 14

Guide de dveloppement logiciel en C++


// X Ajouter un todo // // History : // // 1994/01/01 : Johnny B. Goud : added History //
*** AMLIORATION ***

C++ coding guide

Les symboles O, / et X signifient respectivement que la tche dcrite nest pas commence, est entame et est acheve.
*** IMPRATIF ***

Le cas chant, un copyright doit tre indiqu explicitement. Pour distribuer librement un programme, inclure un avertissement et un copyright dgageant lauteur de toute responsabilit. (Comment ?) Voir le fichier Conditions fourni avec toutes les distributions de la Free Software Foundation. Ce texte est directement disponible sous emacs avec la commande C-h C-c. Le fichier dentte ( .hh ) Le fichier dentte contient les dclarations du programme. Lorientation objet fait de lui le fichier central du dveloppement : il dcrit l'interface de chaque classe.
*** IMPRATIF ***

Chaque fichier dentte (header) doit contenir un mcanisme vitant que son contenu ne soit utilis deux fois lors dune compilation. (Pourquoi ?) Soient deux classes dclares dans deux fichiers dentte diffrents. Tous deux incluent le mme entte XXX.hh. Si un fichier inclut ces deux fichiers dentte de dpart, il inclura indirectement deux fois le contenu de XXX.hh. Ceci poserait de problmes : redfinition de constantes de prprocesseur, double dclaration de classe... Un mcanisme gre ces difficults de manire transparente : une constante de prprocesseur est dfinie lors de la premire lecture du code. Un test sur la dfinition de cette variable permet de ne pas tenir compte du contenu du fichier dentte partir de la deuxime lecture. (Rappel) Noter que ce mcanisme permet dinclure plusieurs fois le mme fichier lors de la compilation dun fichier objet (.o). En revanche, il est pas utilisable pour viter que la mme information ne se trouve dans plusieurs fichiers objets dun mme projet. Par exemple, une instanciation de variable globale, comme une donnes membre statique, ne doit jamais se trouver dans un fichier dentte. (Exemple) Ce mcanisme simple est toujours le mme. Voici un exemple pour une classe Foo, dfinie dans les fichiers Foo.hh et Foo.cc :
// Fichier Foo.hh #if !defined(Foo_hh) #define Foo_hh // CONTENU DU HEADER Foo.hh #endif // !defined(Foo_hh)

(Exemple) Ce mme mcanisme peur aussi tre employ avec une syntaxe lgrement diffrente :
// Fichier Foo.hh

Timothe Royer (royer@uranus.crosat.fr)

version : 1997-02-25

page 15

Guide de dveloppement logiciel en C++


#ifndef Foo_hh #define Foo_hh // CONTENU DU HEADER Foo.hh #endif // ifndef Foo_hh

C++ coding guide

(Exception) Le fichier standard assert.h ne comprend pas ce mcanisme. Ceci permet


*** IMPRATIF ***

Un fichier dentte ne doit rien contenir (sauf commentaires) avant #if !defined XXX_hh ni aprs #endif // XXX.hh .
*** AMLIORATION ***

Utiliser un mcanisme qui dtecte l'inclusion d'un fichier d'entte par lui-mme, directement ou non. (Exemple) FIXME
*** AMLIORATION ***

Pour un gros projet, il est peut tre souhaitable de ne pas inclure de fichier d'entte utilisateur dans un fichier d'entte. (Pourquoi?) Dans un gros projet, plusieurs classes peuvent s'inclure mutuellement. Dans ce cas, deux problmes surgissent : d'une part des inclusions mutuelles de fichier d'entte provoquent des problmes lors de la compilation et d'autre part les temps de recompilation du projet deviennent pnibles. En effet, la plupart des fichiers d'enttes sont inclus par la plupart des fichiers de dfinition. Chacun de ceux-ci doivent tre recompils chaque fois que l'un de ceux l est modifi. A noter que cette difficult syntaxique correspond pour une fois une relle difficult d'annalyse et de conception : la dpendance mutuelle de modules. Le fichier de dfinition ( .cc )
*** IMPRATIF ***

Lorsquun fichier doit inclure plusieurs enttes, il doit imprativement pouvoir les inclure dans nimporte quel ordre sans que cela change quoi que ce soit la compilation ou lexcution. (Pourquoi ?) Cela libre le dveloppeur dune responsabilit inutile, dont la difficult crot exponentiellement avec la taille du projet.
*** RECOMMANDATION ***

Le fichier source, les fichiers objets .o et les excutables doivent rpondre la commande what(1) unix. (Pourquoi ?) Ceci permet de savoir quoi servent les fichier sans avoir les ouvrir. (Comment ?) Il faut que chaque fichier de dfinition contienne une chane de caractres dcrivant lutilit du fichier. Cette chane doit tre prcde de la squence escape @(#). La chane de caractres est affecte une variable globale statique. (Exemple) Voici un exemple de ligne dinformation :
static const char* const Autodescription = "@(#)Projet Pipo-Mollo. Module de gestion de l'cran. V6.66";

Timothe Royer (royer@uranus.crosat.fr)

version : 1997-02-25

page 16

Guide de dveloppement logiciel en C++

C++ coding guide

Lors de la compilation de ce fichier bimbo.c vers un bimbo.o et de ldition de liens vers un a.out, lappel la commande produira la sortie suivante : commande :
what bimbo.c b imbo.o a.out

affichage :
bimbo.c : Projet Pipo-Mollo. Module de gestion de l'cran. bimbo.o Projet Pipo-Mollo. Module de gestion de l'cran. a.out Projet Pipo-Mollo. Module de gestion de l'cran.

Prsentation dinstructions
*** IMPRATIF ***

Utiliser syntaxiquement un pointeur sur fonction comme un nom de fonction. (Exemple) Ces deux notations pour lappel de fonction avec un pointeur sont correctes. La seconde est plus lisible que la premire.
int f(int); int (*pf)(int) (*pf)(999); // // // pf(333); //
*** IMPRATIF ***

= f; Notatio n inutilement surcharge Son unique intrt est de diffrencier explicitement un pointeur sur fonction. Notation claire.

Ne pas utiliser l'oprateur , . (Pourquoi ?) Le seul intrt de loprateur , est de rduire syntaxiquement la taille du code source, au dpend de la lisibilit. (Exemple) Voici un exemple dutilisation de loprateur ,, dont lusage nest jamais souhaitable.
for(int leCompteur=0,someDesNPremiersEntiers=0;leCompteur<x; leCompteur++,sommeDes NPremiersEntiers+=leCompteur);

Par exemple, cet algorithme aurait pu tre cod ainsi :


long sommeDesNPremiersEntiers = 0; for(long leCompteur = 0; leCompteur < thatIndice; leCompteur++)) { sommeDesNPremiersEntiers += leCompteur; }
*** RECOMMANDATION ***

Laisser un espace avant et aprs chaque oprateur.


*** RECOMMANDATION ***

Utiliser abondamment les parenthses pour signifier les priorits au sein dune expression. (Pourquoi ?) La priorit et lassociativit des nombreux oprateurs hrits du C est difficile grer et maintenir. De plus, un oprateur peut tre redfini, mais sa priorit ne peut tre Timothe Royer (royer@uranus.crosat.fr) version : 1997-02-25 page 17

Guide de dveloppement logiciel en C++

C++ coding guide

change. Cela peut poser des problmes lorsque le sens logique dun oprateur change : cest le cas en particulier des oprateurs qui ont t choisis pour dsigner les flux : << et >>. Leur priorit est trs forte car ils taient destins lorigine au dcalage de bits. Or elle devrait tre faible pour permettre aisment laffichage dinstructions. Lusage de parenthses lve les ambiguts.

Nommage
Dans les paragraphes suivant, nous allons tudier des mthodes pour dterminer le nom des identificateurs. Cet aspect de la programmation est fondamental pour la lisibilit et donc la maintenance de code. Majuscules et minuscules
*** IMPRATIF ***

Les identificateurs de donnes constantes globales doivent tre crites entirement en majuscule, les mots spars par des caractres de soulignement. (Exemple) EN_MAJUSCULES_SEPAREES_PAR_DES_UNDERSCORES ;
*** RECOMMANDATION ***

Les variables doivent tre crites en majuscules et minuscules selon la rgle suivante : Variables locales (dont paramtres) : enMinusculesLesMotsSeparesParDesMajuscules ; Le reste (Nom de classe, de donne membre, de mthode et de fonction, instance globale non constante) : ChaqueMotCommenceParUneMajuscule.
*** IMPRATIF ***

Aucun identificateur dfini par lutilisateur ne doit contenir deux caractres souligns successifs ( __ ). (Pourquoi ?) Ces noms sont rservs aux librairies standards pour viter les conflits de nommage [Stroustrup 1991]. (Pourquoi ?) Une des difficults de la compilation du C++ est lusage dun diteur de liens (linker) correspondant un standard ancien. Pour contourner le problme que pose la surcharge de fonction (overload), le compilateur modifie le nom des identificateurs en leur ajoutant en particulier des caractres souligns (_). Des conflits de nommage peuvent apparatre si lutilisateur dfinit des noms commenant par deux caractres de soulignement. De plus, cette prsentation gne la lisibilit. Il est impensable que deux identificateurs se diffrencient simplement par un caractre de soulignement cause dun risque vident de confusion par le programmeur. Ce caractre peut donc tre supprim.
*** RECOMMANDATION ***

Ne pas diffrencier deux identificateurs sur un caractre soulign en plus ou en moins. Ne pas diffrencier deux identificateurs en changeant la casse de certains caractres. (Exemple) Exemple de noms trop proches lun de lautre :
int Tuttle ; char Buttle ;

Timothe Royer (royer@uranus.crosat.fr)

version : 1997-02-25

page 18

Guide de dveloppement logiciel en C++ Choix des identificateurs


*** RECOMMANDATION ***

C++ coding guide

viter les abbrviations et llision des conjonctions dans le choix dun identificateur. (Pourquoi ?) Pour ne pas retaper trop souvent des noms longs et employer ainsi des identificateurs explicites, utiliser un diteur de texte comme emacs (ou mme vi) qui permet la compltion dynamique des noms de variables en cours ddition. (Exemple) Exemples et contre-exemples :
int nbAuto; // autoriss ? automatiques ? automates ? int nbMobilesAutomatiques; for(int i = 0; i < s; i++) // ... for(int mobileCounter = 0; mobileCounter < size; mobileCounter++) // ...
*** RECOMMANDATION ***

Le nom dun identificateur ne doit pas comprendre de ngation [McConnell 1993]. (Exception) Notons cependant que certains cas spcifiques font exception cette rgle, comme une constante de preprocessing utilise pour une compilation optionnelle, qui ne sera pas dfinie par dfaut. (Exemple) Exemples et contre-exemples : FIXME meilleur exemple
// Demande un effort supplmentaire pour la ngation bool notFinished = true; while(notFinished) // ... // Notation plus immdiate : bool keyInFinished = false; while(!keyInFinished) // ...
*** RECOMMANDATION ***

Adopter un systme rgulier de nommage des donnes membres et de leurs mthodes d'accs. (Exemple) Voici une premire possibilit :
Classe Humain { ... public : // Data access long Id(void) const; const String& Name(void) const; void Rename(const String& newName); ... private: // Datas long IdData; String NameData; };

(Exemple) Voici une seconde possibilit :


Classe Humain { ... public : // Data access long GerId(void) const; const String& GetName(void) const;

Timothe Royer (royer@uranus.crosat.fr)

version : 1997-02-25

page 19

Guide de dveloppement logiciel en C++


void SetName(const String& newName); ... private: // Datas long Id; String Name; };
*** RECOMMANDATION ***

C++ coding guide

Nommer les instances et les mthodes de manire ce que ces deux noms accols lors dun appel forme une suite de mots ayant un sens. (Exemple) Voici un exemple :
AddressArray.IsSorted();

Les commentaires
*** IMPRATIF ***

Un commentaire est prcd et suivi dune ligne vide.


*** IMPRATIF ***

Un commentaire structur prcde chaque fonction importante, pour la dcrire. Il se prsente en trois parties : Description de leffet de la fonction. Description des paramtres, le cas chant ; Description de la valeur retourne, au besoin ; (Exemple) Voici un exemple de commentaire :
// Function : QuickSort // // Purpose : Sorts an array of element by swaping them. // // Parameters : // thatAreaStart : Element's array address. // thatNumberOfElements : Number of elements in the array. // thatElementSize : Size of one element in bits. // thatComparisonFunction : fonction comparing two elements. It // must returns 0 if they are equal, 1 if the first one is // smaller and -1 otherwise. // // Returns : Nothing void QuickSort( void* thatAreaStart, const int thatNumberOfElements, int thatElementSize, int (*thatComparisonFunction) (const void* const, const void* const)) { // DEEP MAGIC HERE k :) }
*** IMPRATIF 2.5.3 ***

Les commentaires ne doivent pas se trouver sur la mme ligne quune instruction.

Timothe Royer (royer@uranus.crosat.fr)

version : 1997-02-25

page 20

Guide de dveloppement logiciel en C++


*** RECOMMANDATION ***

C++ coding guide

Il ne doit pas y avoir de commentaires dans le corps dune fonction. (Pourquoi ?) Si le code source est bien crit, les noms didentificateurs auto-descriptifs suffisent comprendre le programme. En revanche, il ne devrait pas tre ncessaire de lire un code pour savoir ce quil fait. Des commentaires concis (une phrase suffit souvent) doivent : Se trouver en dbut de chaque fichier, pour indiquer son contenu ; Avant chaque fonction, pour indiquer son intrt et son interface. (Exception) Bien sr, lorsquun algorithme compliqu est utilis, la thorie peut ne pas transparatre dans limplmentation. Des commentaires peuvent alors tre ajouts. Cette situation reste trs exceptionnelle. (Exemple) Une autre raison de ne pas mettre de commentaires en bout de ligne. Quel est le problme ? FIXME test + expl
#define XTKP // Extraction toolkit procedure
*** IMPRATIF ***

Pour indiquer un commentaire, utiliser seulement //. Ne pas utiliser les dlimiteurs /* et */. (Pourquoi ?) Il y a plusieurs raisons pour cela. Tout dabord il faut viter dimbriquer les commentaires : certains compilateurs lautorisent alors que la norme linterdit [Stroustrup 1991]. De plus, les commentaires ne doivent pas tre longs. Si une partie du code est mise en commentaire, cela doit tre fait explicitement. Sinon, du code mis en commentaire risque dtre modifi lors de la maintenance. Eventuellement, pour masquer trs temporairement une partie du code source, utiliser la directive de prcompilateur #if 0. (Exemple) Exemples de bonnes et de mauvaises prsentations de commentaires :
// // Cette fonction calcule l'ge du capitaine. // /* Sommes-nous dans un commentaires ? */ /* ** Cette fonction taille un biface. */ /* Cette fonction est srement bugge.*/

Seule la premire prsentation est bonne. La troisime est adapte au C.


*** RECOMMANDATION ***

Les commentaires dans une fonction doivent rester exceptionnels. (Pourquoi ?)Ils ne sont utiles que pour dcrire un algorithme compliqu. Mis part en recherche ou en programmation systme, lalgorithmique employe est simple voire lmentaire. Un mauvais codage rend la fonction difficile lire et motive les commentaires. Ce nest pas une bonne dmarche. Le programmeur doit plutt produire une implmentation claire et un corps de fonction autodescriptif.
*** RECOMMANDATION ***

Les commentaires sont indents : ils commencent la premire colonne sils ne se trouvent pas dans une fonction. Timothe Royer (royer@uranus.crosat.fr) version : 1997-02-25 page 21

Guide de dveloppement logiciel en C++

C++ coding guide

Chapitre 3 - Structuration logique du programme


Thus spake the master programmer : "A well-written program is its own heaven; a poorly-written program its own hell." The Tao of Programming.

Les variables
Nous allons prsenter dans les paragraphes suivants les recommandations concernant la dclaration de donnes en gnral, pour les types prdfinis et enfin pour les types utilisateur simples. Recommandations communes tous les types
*** IMPRATIF ***

Une variable ne doit pas en masquer une autre, ce qui se produit lorsque deux variables ont le mme nom et se trouvent dans le mme espace (scope). (Pourquoi ?) Lorsque plusieurs informations sont designes par le mme nom et ne sont diffrentiables que par leur position par rapport aux structures de contrle, cela rduit la lisibilit du code et favorise lapparition derreurs, en particulier lors de la maintenance du source. (Exemple) Voici un extrait de source :
int i = 0; // ... { int i = 0; // ... i++; // ... }

Si la deuxime dclaration de i est supprime, alors il y a un risque pour que lincrmentation ne le soit pas. Le code resterait valide et le compilateur ne peut dtecter aucun problme.
*** AMLIORATION ***

Une variable doit tre dfinie dans le plus petit espace de nommage dans lequel elle est utile.
*** AMLIORATION ***

Ne rien affecter un identificateur de tableau. (Exemple) Les types int a[] et int* a sont compltement diffrents, mme si, une fois dclars, leur usage est identique. Le programmeur doit tre particulirement vigilant : par exemple, beaucoup dditeurs de liens confondraient un int* a et un int a[] dfinis dans deux modules (.o) diffrents. Ceci est susceptible de provoquer une erreur fatale. (Pourquoi ?) Voici une illustration de la diffrence entre ces deux types [C FAQ] : Timothe Royer (royer@uranus.crosat.fr) version : 1997-02-25 page 22

Guide de dveloppement logiciel en C++


char a[] = "hello"; char* p = "hello";

C++ coding guide

La variable a occupe 6 octets dans lespace de la mmoire dynamique. Cette zone sera dsalloue lorsque la variable sortira de son espace de validit. La variable p occupe 4 octets (taille courrante dun pointeur). Elle est un pointeur qui rfrence une rgion de la mmoire non modifiable. Une nouvelle valeur peut tre affecte p, mais pas a. En fait, un bon compilateur devrait imposer ici le type const char*. Les types prdfinis
*** IMPRATIF ***

Utiliser le type standard bool, ainsi que les constantes true et false. (Comment?) Ce type fait dsormais partie du langage C++. Son existance peut tre simule pour un compilateur trop ancien.
typedef int bool; const bool true = (0==0); const bool false = !true;
*** RECOMMANDATION ***

Ne pas utiliser de champs de bits. (Pourquoi ?) Contrairement une ide reue ancienne, lusage de champs de bits augmente la taille du code et son temps dexcution. Il rduit aussi beaucoup la portabilit et la lisibilit du code. Cet exemple est reprsentatif des mauvais choix technologiques souvent justifis par un besoin doptimisation. Lalignement de champs de bits, comme le signe par dfaut de ces champs dpend de limplmentation.
*** RECOMMANDATION ***

Ne pas utiliser le type void*. (Pourquoi ?) Lemploi du type (void*) enlve le bnfice du contrle de type. Son usage est justifi en C pour la manipulation de zones mmoires avec malloc, par exemple. Ceci na pas dintrt en C++ o le pointeur retourn par new est typ. (Rappel) Le type (void*) permet de dsigner une zone mmoire sans prsomption de son contenu. Cette notion nest plus ni utile ni souhaitable dans un environnement objet.
*** RECOMMANDATION ***

Ne pas supposer que le type char contient une donne signe. (Pourquoi ?) Prciser signed ou unsigned si les 8 bits du type sont utiliss. Le cas choisi par dfaut dpend de limplmentation. (Exemple) Dfinir et utiliser ces deux types si vous avez besoin de savoir si un char est sign ou non :
typedef unsigned char uchar; typedef signed char schar;
*** AMLIORATION ***

Comme type entier, nutiliser que long et comme type numrique virgule, nutiliser que double.

Timothe Royer (royer@uranus.crosat.fr)

version : 1997-02-25

page 23

Guide de dveloppement logiciel en C++

C++ coding guide

(Pourquoi ?) la diffrence de int, long est le plus souvent cod sur 4 octets, sur les machines 16 bits comme 32 bits. double est toujours plus prcis que float. Rduire le nombre de types de base utiliss vite les casts automatiques, difficiles matriser en C. De plus, si une valeur ne doit jamais tre ngative, il est plus sr de la coder sur un format de donne sign et de vrifier quelle reste positive. Enfin, la librairie mathmatique standard c travaille en double. (Exception) Bien sr, dans certains cas limites bien identifis, le temps d'excution est plus important que la scurisation et la portabilit du code. Il peut alors tre ncessaire de faire appel l'ensemble des types de base que propose le C++. Les types utilisateurs simples
*** RECOMMANDATION ***

Les seules variables globales utiliser sont les donnes constantes, sous forme de type simple (sans constructeur) et const.
*** RECOMMANDATION ***

Lorsquune variable doit tre dclare globale (pour une raison srement indpendante de la volont du programmeur : compatibilit ascendante ou maintenance de code par exemple), elle doit tre dclare extern dans le header (fichier *.hh) et instancie rellement dans le code (fichier *.cc). (Pourquoi ?) Si la variable est instancie dans le header et que le header est inclus plusieurs fois, alors il pourra y avoir un problme lors de ldition de liens. Certains compilateurs tolrent cette situation, mais elle nest bien sr pas souhaitable. Si la variable nest pas dclare extern dans le header inclure au besoin, il faudra la dclarer extern avant chaque usage, dans chaque fichier qui utilise la variable, ce qui est pnible maintenir. Accessoirement, cette recommandation permet de modifier linitialisation de la variable en minimisant les recompilations.
*** RECOMMANDATION ***

Lexistence dun trs petit nombre de donnes globales peut tre tolre. Il faut les dfinir comme membre static.
*** IMPRATIF ***

Les pointeurs doivent tre dclars en employant const de la manire la plus stricte possible. (Rappel) Les pointeurs peuvent tre constants ou non et pointer sur des donnes constantes ou non. Ce sont deux paramtres indpendants. Un pointeur constant scrit void* const et un pointeur sur une donne constante scrit : const Type*. (Pourquoi ?) Utiliser ce mcanisme est fondamental : pour la scurit du code : lorsque j'ai apliqu ce mcanisme pour la premire fois, il m'a permis de dtecter une bombe retardement qui n'avait pas explos lors des tests. Il tait du type :
void foo(int index) { if(index = 0) //... }

Timothe Royer (royer@uranus.crosat.fr)

version : 1997-02-25

page 24

Guide de dveloppement logiciel en C++

C++ coding guide

Bien sr, le paramtre pass par valeur n'tait pas destin tre modifi. En le manipulant comme const en redfinissant l'entte de la fonction ainsi : void foo(const int index) est apparu le problme lors de la recompilation : j'avais utilis par inadvertance l'oprateur = la place de l'oprateur ==; pour la maintenance : Savoir si une donne est destine tre modifie aprs son initialisation est une information prcieuse qu'il n'est pas toujours ais de dterminer lors de la maintenance. Le mcanisme const fourni cette information et garanti mme que les donnes en lecture seule ne seront pas modifies, quelle que soit la manire dont on y accde (pointeur, passage de paramtre par rfrence, hritage...). (Exemple) Voici quelques illustrations :
// Donne constante : const int nombreDeChromosomesHumains = 23; // Pointeur fix sur une donnes constante : const char* const TITRE = "Oui-oui au pays de la gomme magique"; // Pointeur variable sur une donne constante : const char* message = "SYNTAX ERROR. OK."; // Pointeur fix sur une donne variable : char* const referencedUntilDelete= new char[99];

Les structures de contrle


*** AMLIORATION ***

Lespace de validit dune variable dfinie entre les parenthses qui suivent une instruction for a chang : dsormais, cette donne nest plus accessible que dans la boucle, alors quelle ltait aussi lextrieur de la boucle auparavant. (Exemple) Le code suivant nest plus valide :
for(int i = 0; i < HeureCourante; i++) cout << "coucou"; for(i = 0; i < MinuteCourante; i++) cout << "bip";

Ce code correspond aux nouvelles normes, mais ne sera pas compil par un compilateur ancien :
for(int i = 0; i < HeureCourante; i++) cout << "coucou"; for(int i = 0; i < MinuteCourante; i++) cout << "bip";

Cette mthode est valide pour lancienne comme pour la nouvelle norme, mais elle nest pas recommande : lespace de validit des compteurs de la boucle est inutilement important :
int i; for(i = 0; i < HeureCourante; i++) cout << "coucou"; for(i = 0; i < MinuteCourante; i++) cout << "bip";

Cette manire de dclarer les variables de boucle respecte lancienne et la nouvelle norme. Elle est lisible. Celle-ci doit tre utilise.
for(int heureC = 0; heureC < HeureCourante; heureC++) cout << "coucou"; for(int minuteC = 0; minuteC < MinuteCourante; minuteC++) cout << "bip";

Timothe Royer (royer@uranus.crosat.fr)

version : 1997-02-25

page 25

Guide de dveloppement logiciel en C++

C++ coding guide

La classe
*** IMPRATIF ***

Toutes les donnes membres sont prives. (Pourquoi ?) Les donnes membres ne doivent tre modifiables que par des mthodes dinterfaces (accesseurs). (Exemple) Exemple daccs aux donnes membres :
class Capitaine { int Age; public: const int& GetAge(void) const { return Age; } void SetAge(c onst int& thatNewAge) { ASSERT(thatNewAge>0); Age = thatNewAge; } };

Les membres privs peuvent tre redfinis en protg (protected) pour tre accessible aux classes drives.
*** IMPRATIF ***

Les donnes membres dont les valeurs sont fixes linstanciation de la classe, et ne doivent plus tre modifies ensuite, sont dfinies comme constantes. (Exemple) Exemple dutilisation dune donne membre constante durant la vie de lobjet :
class Human { const bool Male; public: Human(const bool& thatIsMa le) : Male(thatIsMale) {} // ... };

Dans cet exemple, le genre du Human instanci est construit avant lentre dans le constructeur. Sa valeur est fixe par thatIsMale.
*** IMPRATIF ***

Cacher les mthodes gnres automatiquement par le compilateur, si elles ne sont pas dfinies. Ces mthodes sont : le constructeur vide, le constructeur par copie, l'oprateur = et le destructeur vide. (Pourquoi ?) Ces mthodes sont une des failles de limplmentation des objets en C++. Ces quatre mthodes sont dfinies pour les struct du C et ont t maintenues pour les classes dans un souci de compatibilit. Si elles ne sont pas dfinies pour tre utilises, le dveloppeur doit les masquer pour sassurer quelles ne seront pas appeles par inadvertance. Comme pour beaucoup de propositions de ce guide, celle-ci prend bien sr toute son importance dans un gros projet, o lutilisateur dune classe nest pas toujours celui qui la crite et peut ne pas savoir que le constructeur par copie qui est appel lors dun passage de paramtres nest pas dfinit et produit un rsultat alatoire. (Exemple) Voici une technique permettant de masquer ces mthodes lorsquelles ne sont pas dfinies. Dune part, elles doivent tre masques pour lextrieur de la classe. Pour cela, il suffit de les dclarer private. Ne pas dfinir leur corps. Dautre part, elles doivent aussi tre masques lintrieur de la classe. Pour cela, il faut les dclarer inline et ne pas dfinir leur corps. En fait il nest pas ncessaire de les dclarer inline, mais dans ce cas, si lon tente Timothe Royer (royer@uranus.crosat.fr) version : 1997-02-25 page 26

Guide de dveloppement logiciel en C++

C++ coding guide

dutiliser une mthode masque, le message derreur produit la compilation est beaucoup plus clair : il est indiqu lappel de la mthode et non pas plus tard, lors de ldition de lien o les messages derreur sont beaucoup moins prcis. cf. Annexe C - Exemple de prsentation (Exemple) Voici une implmentation lmentaire dune classe String. Lusage qui en est fait ici provoque une erreur fatale car les mthodes gnres par le compilateur sont utilises, alors quelles nont pas t dfinies.
// String.hh : class String { char* Data; public: String(void); String(char* const thatCreator); ~String(void); void SelfDisplay(void) const; }; // String.cc : String::String(void) { Data = new char[1]; Data[0] = '\0'; } String::String(char* const thatCreator) { Data = new char[strlen(thatCreator)+1]; strcpy(Data,thatCreator); } String::~String(void) { delete[] Data; } void String::SelfDisplay(void) const { if(Data) { cout << Data; } else { cout << "( null)"; } } void DisplayString(const String thatString2Display) { thatString2Display.SelfDisplay(); } int main(void) { String myName("Foo Bar"); DisplayString(myName); return 0; }

Dans ce cas, une String myName est instancie, puis passe en paramtre par valeur. Une String thatString2Display est instancie en entrant dans DisplayString, par appel au constructeur par copie. Ceci est fait par le compilateur de manire transparente. Or, ce constructeur qui prend une String& en paramtre nest pas dfini. Il est donc dfini automatiquement par le compilateur pour effectuer une copie membre membre. Les donnes Data de myName et de thatString2Display pointent donc sur la mme zone mmoire. Ensuite, Timothe Royer (royer@uranus.crosat.fr) version : 1997-02-25 page 27

Guide de dveloppement logiciel en C++

C++ coding guide

la sortie de DisplayString(), thatString2Display est dtruit. La zone pointe par son Data est dsalloue lors de lexcution de son destructeur. myName rfrence dsormais une zone dsalloue. (Rappel) Noter dautre part la diffrence entre le constructeur par copie et loprateur =(). Les notations String titi(3), String toto( hello ), String tutu(toto) ou String tata( boo ,10) utilisent le constructeur avec paramtres. Or les trois premires dclarations peuvent aussi scrire : String titi = 3, String toto = hello et String tutu=toto. Dans ces trois cas, ce sont les constructeurs avec paramtres qui sont utiliss. Pour appeler le constructeur vide, il faut employer la notation suivante : String titi ; titi = 3 ; ou String toto ; toto = hello ou encore String tutu ; tutu = toto. Lappel au constructeur avec le signe = nest possible que pour les constructeurs recevant un unique paramtre. La notation du constructeur avec le signe = est utilise principalement pour la construction par copie et la notation avec parenthses est utilise dans les autres cas. FIXME + clair
*** RECOMMANDE ***

La dclaration vide de classe nest pas souhaitable. (Rappel) Il est possible de signaler lexistence dune classe sans la dclarer. Il suffit pour cela de faire prcder le nom de la classe du mot-clef class. Exemple : class Mollo ; . Cette simple dclaration ne permet bien sr pas de prsumer quoi que ce soit de la classe, mais permet dutiliser des pointeurs sur la classe. (Pourquoi ?) Lapparition de ces indications de classes dans un code source montre un problme de dcoupage du code source en fichier. (Comment ?) Il faut inclure le fichier dentte dclarant la classe utiliser. (Exception) Dans deux cas, la dclaration vide dune classe est ncessaire : Linclusion mutuelle de deux classes; Lorsque l'on veut viter que trop de fichiers d'entte s'incluent mutuellement pour rduire le temps de recompilation d'un projet important. (Exemple) Voici un exemple illustrant les cas pour lesquels une dclaration vide est utile : les inclusions mutuelles.
// Agent.hh : #if !defined(Agent_hh) #define Agent_hh class Word; class Agent { World MyWorld; public: Agent(World& _myNativeWorld); void Think(void); // ... }; #endif // !defined(Agent_hh) // Agent.cc : #include "Agent.hh" #include <World.hh> Agent::Agent(World& _myNativeWorld) {

: MyWorld(_myNativeWorld)

Timothe Royer (royer@uranus.crosat.fr)

version : 1997-02-25

page 28

Guide de dveloppement logiciel en C++


} void Agent::Think(void) { // ... currentWeather = MyWorld.AskForTheWeather(); // ... } // World.hh : #if !defined(World_hh) #define World_hh class Agent; class World { Agent** Population; long PopulationSize; // ... inline void OneTurn(void); }; #include <Agent.hh> void World::OneTurn(void) { for(popCounter = 0; popCounter < PopulationSize; popCounter++) { Population[popCounter]->Think(); } } #endif // !defined(World_hh)
*** RECOMMANDATION ***

C++ coding guide

Il faut viter de dfinir plusieurs oprateurs de transtypage pour une classe. (Exemple) Voici une mauvaise utilisation du mcanisme de surcharge :
class String { char* Data; //... operator const char*(void) const { return Data; } operator int(void) const { return atoi(Data); } //... };

Une classe String dfinie comme ci-dessus peut paratre ergonomique. En fait plusieurs problmes apparaissent. Il est interdit de passer directement une instance de cette classe une fonction surcharge pour recevoir un const char* et un int. Il faut prciser l'oprateur de cast choisi.
*** IMPRATIF ***

Indiquer inline, static ou virtual uniquement dans la dclaration de la classe, et pas la dfinition de la mthode.
*** RECOMMANDATION ***

Ne pas inclure le corps d'une mthode dans la dclaration d'une classe. Si une fonction doit tre dclare inline, mettre au besoin sa dfinition dans le header, aprs la dfinition de la classe.
*** RECOMMANDATION ***

viter d'utiliser le mot clef friend.

Timothe Royer (royer@uranus.crosat.fr)

version : 1997-02-25

page 29

Guide de dveloppement logiciel en C++

C++ coding guide

(Pourquoi?) Ce mcanisme brise l'encapsulation des donnes. L'intrt de la scurisation des mthodes d'interface diminue pour une classe qui a des friends. Il est cependant prferable de lui ajouter un "friend" pour ne pas avoir passer des membres de private public.
*** AMLIORATION ***

viter d'utiliser le mot-clef protected, en particulier pour qualifier une donne membre. (Pourquoi ?) Bjarne Stroustrup, instigateur du C++, considre dans son dernier livre [Stroustrup 1994] que le mcanisme protected naurait pas d tre implment en C++. Il sera maintenu pour garantir la prennit des codes existant mais son usage nest pas recommand. (Comment ?) Les mthodes dinterface suffisent dans beaucoup de cas.
*** AMLIORATION ***

Indiquer les membres publics, puis les membres privs. (Exemple) cf. XXX.hh.
*** RECOMMANDATION ***

Disposer les mthodes dans le mme ordre dans le fichier d'entte que dans le fichier de dfinitions. (Pourquoi?) Pour maintenir la mme logique, comme pour faciliter la recherche d'une dfinition de mthode.
*** RECOMMANDATION ***

viter dutiliser une instance globale dans un constructeur. (Pourquoi ?) Linstance globale peut ne pas avoir t construite lors de son utilisation par un constructeur, dans le cas o ce constructeur est appel pour instancier une autre globale. (Exemple) Metaproblme classique illustr dans un nouveau systme formel [Ellemtel-1992] :
class Poule; class OEuf { public: const char* Nom; OEuf(const char* const MonNom, const Poule& Mere); }; class Poule { public: const char* Nom; Poule(const char* const MonNom, const OEuf& Origine) : Nom(MonNom) { cout << "Je m'appelle "<< Nom ; cout << " et je proviens de l'oeuf "; cout << Origine.Nom << endl; } }; OEuf::OEuf(const char* const MonNom, const Poule& Mere): Nom(MonNom) { cout << "Je m'appelle "<< Nom ; cout << " et ma mere est " << Mere.Nom << endl; } extern Poule PremierePoule;

Timothe Royer (royer@uranus.crosat.fr)

version : 1997-02-25

page 30

Guide de dveloppement logiciel en C++


OEuf PremierOEuf("Cali",PremierePoule); Poule PremierePoule("Mero",PremierOEuf); int main(void) { return 0; }

C++ coding guide

Ce programme compile sans aucun avertissement. Lors de lexcution de ce programme, deux instances globales seront construites avant lentre dans la fonction main() : PremierOeuf et PremierePoule. Il ny a pas de moyen de dterminer lequel sera instanci en premier. Celui qui est construit en premier fait rfrence au nom de lautre. Or, ce nom nest pas encore initialis car le constructeur qui doit le faire na pas encore t excut. Un pointeur invalide est donc drfrenc. (Rappel) De plus le code du constructeur dune instance globale sexcute avant lentre dans le main(). Le code de son destructeur sexcute aprs la fin du main(). Cela complique la comprhension du code source. NB : Je ne connais pas de compilateur signalant ce problme. Or, il peut napparatre que tard au cours dun dveloppement : lors dun changement de ldition de lien ou dun portage. Il peut tre difficile dtecter dans une application importante.
*** RECOMMANDATION ***

Chaque classe comprend une mthode OK() vrifiant un invariant dfinissant la validit dune instance. Elle utilise les mthodes OK() de ses instances membres. (Exemple) Voici un exemple qui permet de tester lintgrit dune classe String :
class String { long Size; char* Data; // ... public : bool OK(void) { bool iAmHealthy = false; for(long charCount = 0; charCount < Size; charCount++) { if(!Data[charCount]) { iAmHealthy = true; } } #if !defined NO_DEBUG if(!iAmHealthy) { cerr << "String::OK() failed."<< endl; cerr << "Char string is not null terminated."; cerr << endl; ASSERT(0); } #endif // !defined NO_DEBUG return iAmHealthy; } };

Il peut tre intressant de pouvoir lancer interactivement la mthode OK() de chacune des instances pour vrifier au besoin la validit de ltat des donnes au sein du programme, en particulier lorsqu' un problme survient. Timothe Royer (royer@uranus.crosat.fr) version : 1997-02-25 page 31

Guide de dveloppement logiciel en C++


*** RECOMMANDATION ***

C++ coding guide

viter de dfinir un constructeur recevant un argument, lorsque sa logique ne correspond pas un clonage. Le compilateur risque dy faire appel implicitement. (Exemple) Voici par exemple, deux constructeurs pour une classe String. Lun est souhaitable, lautre pas.
class String { // ... public : String(const char* const thatClone); String(const long thatSize); // ... SelfDisplay(ostream& thatStream); // ... }; void DisplayString(const String& thatStringToDisplay) { thatStringToDisplay.SelfDisplay(cout); } int main(void) { const char* const paradigmaticString = "Hello world !\n"; const long paradigmaticSize = 25 6; DisplayString(paradigmaticString); DisplayString(paradigmaticSize); return 0; }

Lors de lexcution de ce programme, la fonction DisplayString est appele deux fois. Lors du premier appel, une variable temporaire de type String est construite par le constructeur de String recevant un const char* const. Tout se passe bien : Hello world ! apparat lcran. Lors du deuxime appel, la String temporaire est construite partir dun entier long. tait-ce vraiment le but recherch ? De toutes faons, lindication dune taille de String est un problme dont lutilisateur de la classe ne doit jamais avoir se soucier.

Les fonctions et les mthodes


Recommandations communes aux fonctions et aux mthodes
*** IMPRATIF ***

Toujours indiquer explicitement le type de la valeur de retour. Utiliser void si la fonction ne renvoie rien.
*** IMPRATIF ***

Toujours coller la parenthse gauche dune fonction au nom de celle-ci. (Pourquoi ?) Cette rgle est reprsentative de la rigueur avec laquelle un fichier source doit tre crit, surtout pour un projet important. Par exemple, lors de la maintenance, le nom d'une fonction peut tre modifie. Si tous les appels respectent la mme prsentation, il est ais de faire une recherche automatique sur tous les appels, pour vrifier que les modifications respectent lusage qui tait fait de la mthode jusque l. Ceci grce une commande du type (un*x) : grep \.MethodeRecherchee( `find $PROJECT -name *.[hc]* -print` Timothe Royer (royer@uranus.crosat.fr) version : 1997-02-25 page 32

Guide de dveloppement logiciel en C++


*** IMPRATIF ***

C++ coding guide

Prsenter le prototype des fonctions avec les noms de paramtre de la mme manire que pour leur dclaration.
*** IMPRATIF ***

Chaque fonction dfinie par lutilisateur doit tre prcde dun prototype.
*** RECOMMANDATION ***

Une fonction ne doit pas tre longue de plus de 100 lignes. (Pourquoi ?) Ceci pour des raisons videntes de lisibilit et de maintenance. Des tudes statistiques prcises [CC FIXME] ont montr que le nombre de bugs par fonction crot exponentiellement avec la taille de celle-ci, partir dun certain nombre de lignes. (Exception) Quelques fonctions peuvent dpasser cette taille, pour des besoins exceptionnels. En particulier, des fonctions concernant des interfaces homme-machine fondes sur des librairies lourdes mettre en oeuvre.
*** RECOMMANDATION ***

Tous les paramtres sont reus comme donnes constantes (const), sauf ceux qui sont passs par rfrence et qui sont destins tre modifis.
*** RECOMMANDATION ***

Lorsqu'une fonction doit toujours recevoir une donne positive, utiliser un type sign et tester son signe plutt que utiliser un type non sign.
*** AMLIORATION ***

Lorsquune mthode ne modifie pas linstance dans certains cas, il faut la surcharger sur sa caractristique const. (Rappel) Une fonction peut tre overloade sur sa caractristique const : deux mthodes diffrentes dune mme classe peuvent avoir exactement le mme prototype sauf en ce qui concerne leur caractre const . lexcution, la mthode const sera appele dans la mesure du possible, sinon la mthode non-const sera appele. (Pourquoi ?) Ceci permet : Didentifier les cas o le contenu de la classe a chang ; Dappeler une mthode sur une instance const, alors que la version non-const de cette mthode doit modifier lobjet dans dautres circonstances. (Exemple) Voici une utilisation possible de la surcharge de fonctions :
class String { //... String& operator +=(const char* const thatCharZero2Append); String& operator +=(const char thatChar2Append); String& operator +=(const String thatString2Append); //... };

Dans cet exemple, loprateur +=() doit permettre dans tous les cas de concatner des caractres la fin de la String. (Exemple) FIXME axc tab const / non => const op = const != op() Timothe Royer (royer@uranus.crosat.fr) version : 1997-02-25 page 33

Guide de dveloppement logiciel en C++


*** AMLIORATION ***

C++ coding guide

viter dutiliser plus de un return par fonction ou mthode. (Pourquoi ?) Une fonction qui contient plusieurs returns ne respecte pas les rgles lmentaires de la programmation structure. Elle est difficile maintenir.
*** RECOMMANDATION ***

Le nom des arguments ventuels dune fonction doit tre le mme dans le prototype et dans la dclaration du corps de celle-ci. (Pourquoi ?) Prvenir le code source des commentaires bas niveau est unanimement recommande, mais cette mthode requiert une plus grande rigueur. En particulier en ce qui concerne la dtermination des noms des identificateurs. Fonctions
*** RECOMMANDATION ***

Tout le code doit se trouver dans des mthodes. Il n'est pas souhaitable de dfinir des fonctions sauf dans trois cas prcis : Surcharge doprateurs ; newhandler ; main() Certaines utilisations des STL. (Pourquoi ?) Malgr les habitudes quauront pu prendre les programmeurs en C et en assembleur, il est fortement dconseill de dclarer des fonctions (par opposition aux mthodes). Dans un langage purement objet personne ne pense seulement dclarer un jour une procdure qui ne soit pas une mthode. Ce nest vcu ni comme une brimade, ni comme un entrave la conception dun projet (cf. Smalltalk ou Java). Pour des raisons historiques, le C++ est un langage qui permet de briser facilement le modle objet. Ce nest pourtant pas une pratique recommandable. Cette ide peut sembler nouvelle certains. Elle va pourtant trs loin. Il est mme possible dcrire des logiciels systmes entirement en objets (OS, drivers). Jai ainsi pu travailler sur une phase de boot unix qui avait t entirement (et brillamment) oriente objet.[Detienne 199 ?]
*** AMLIORATION ***

Dclarer les prototypes des fonctions dinterface dans le fichier dentte ( .hh ). Dclarer les prototypes des fonctions internes dans le fichier ( .cc ) qui les dfinit.
*** IMPRATIF ***

Les dpassements de capacit mmoire doivent tre dtects. (Pourquoi ?) Un dpassement de capacit mmoire indique le plus souvent une fuite dans la gestion de la mmoire dynamique (memory leak). Ce problme doit de toute faon tre dtect, surtout lorsque le programme sexcute sous un systme dexploitation qui ne fait pas travailler le processeur en mode protg (DOS ou Windows) : dans ces cas-l, un problme comme un dpassement de capacit mmoire provoque un comportement imprvisible qui peut obliger teindre la machine ou qui peut mme endommager le disque dur. (Exemple) Il existe deux mthodes pour dtecter les dpassements de capacit mmoire. Timothe Royer (royer@uranus.crosat.fr) version : 1997-02-25 page 34

Guide de dveloppement logiciel en C++

C++ coding guide

Une mthode consiste indiquer une fonction utilisateur qui sera appele automatiquement si un dpassement de capacit se produit. Cela se fait simplement en appelant la fonction standard set_new_handler() avec comme paramtre le pointeur sur une fonction utilsateur. Cette fonction doit grer les dpassements de mmoire :
void MyNewHandler(void) { cerr << "Memory exhausted. Sorry." << endl; abort(); } int main(void) { set_new_handler(MyNewHandler); // Le programme principal. return 0; }
*** AMLIORATION ***

La fonction qui gre le dpassement de mmoire essaie de librer de la mmoire et permet de reprendre l'excution, si possible.
*** IMPRATIF ***

La fonction main prend zro, deux ou trois arguments selon les besoins. (Rappel) Voici le type de ces arguments ventuels : int argc : le nombre darguments de la ligne de commande, nom de lexcutable inclus, char* argv[] : tableau de pointeurs sur les chanes/token de la ligne de commande, commenant par le nom de lexcutable et se terminant par (char*)0 ; char* env[] : liste de chanes reprsentant les variables denvironnement.
*** IMPRATIF ***

La fonction main retourne un int : 0 si lexcution sest bien passe, une autre valeur sinon. (Exemple) Tous les programmes retournent une valeur lenvironnement la fin de leur excution. Si le type de la valeur de retour de main est for void, la valeur retourne lenvironnement est indfinie, ce qui peut tre gnant pour un script shell, par exemple. Cette valeur est fixe par un return dans la fonction main ou par un exit depuis nimporte quel endroit. Mthodes
*** IMPRATIF ***

Chaque mthode qui ne modifie pas les donnes de linstance de la classe laquelle elle appartient doit tre const.
*** IMPRATIF ***

Une mthode publique dune classe ne doit retourner, ni un pointeur de membre non constant, ni une rference non constante, sur une donne membre. (Pourquoi ?) Si ces informations sortent de la classe, lencapsulation des donnes est rompue et les donnes prives dune classe peuvent tre modifies sans contrle.
*** AMELIORATION ***

Deux classes ne doivent pas avoir une mthode d'interface qui a le mme nom. Timothe Royer (royer@uranus.crosat.fr) version : 1997-02-25 page 35

Guide de dveloppement logiciel en C++

C++ coding guide

(Exception) Bien sr cette recommandation ne s'applique pas aux classes qui ont un rapport dhritage entre elles. (Pourquoi?) Lorsqu'une mthode est appele au sein d'un gros projet, il est difficile de dterminer quelle classe elle appartient. C'est un danger qu'il faut garder l'esprit lors de l'implmentation. Mais ce n'est pas toujours ralisable dans de bonnes conditions.

Inclusions mutuelles
Lorsque linclusion mutuelle de deux headers nest pas due une erreur danalyse mais correspond bien une logique incontournable, alors lapplication de cette algorithmique particulire doit tre traite avec un soin particulier.
*** RECOMMANDATION ***

viter linclusion mutuelle de deux fichiers dentte. (Pourquoi ?) Programmer deux objets (ou plus !) qui sutilisent mutuellement pose des problmes de conception et doit tre vit dans la mesure du possible. Cette situation rsulte dune algorithmique complexe impliquant en particulier des rcursivit involontaires qui pourraient provoquer des problmes dont lorigine serait difficile dtecter.
*** RECOMMANDATION ***

Lorsque deux classes sutilisent rciproquement : lune des classes ne doit pas utiliser un membre de lautre classe ; lune des classes ne peut contenir que des pointeurs ou des rfrences vers lautre classe ; le constructeur de lune des classes ne doit pas utiliser une instance globale de lautre classe ; le .hh de chaque classe est structur normalement (cf. Annexe) deux exceptions prs : linstruction suivante prcde la dclaration de la classe : class AutreClasse ; et la directive suivante est inclue juste aprs la dclaration de la classe : #include <AutreClasse.hh> . la dclaration de chaque classe est prcde de la ligne : class AutreClasse ;
*** AMELIORATION ***

Utiliser un mcanisme qui permet la dtection de cycles d'inclusions de headers. (Pourquoi ?) FIXME (Comment)
#if defined(TITI_CURRENT) #error Header cycle detected #else // defined(TITI_CURRENT) // Contenu habituel du fichier d'entte #undef TITI_CURRENT #endif // defined(TITI_CURRENT)

Timothe Royer (royer@uranus.crosat.fr)

version : 1997-02-25

page 36

Guide de dveloppement logiciel en C++

C++ coding guide

Le prprocesseur
*** IMPRATIF ***

Lusage de #pragma doit tre vit au maximum, sauf ventuellement dans une zone de compilation optionnelle dpendant du compilateur.
*** AMLIORATION ***

Une macro de prprocessing dans le code source n'a pas besoin d'tre suivi d'un point virgule. Il est possible de l'imposer dans un souci de cohrence syntaxique avec un appel de fonction. (Comment?) Il suffit d'insrer le corps de la macro dans une boucle do {...} while(0). (Exemple) Voici comment donner l'illusion d'une fonction magique qui implmenterait le mcanisme d'assertion :
#define assert(X) do \ { \ if(!(X)) \ { \ cerr << "Assertion failed : (" << #X << ')'; \ cerr << endl << "In file : " << __FILE__; \ cerr << "at line #" << __LINE__ << endl; \ abort();} \ } \ } while(0)

(Rappel) La structure do {...} while(...) doit tre suivie d'un point virgule. Le "while(0)" est bien sr limin de l'excutable par tout compilateur digne de ce nom.
*** AMLIORATION ***

Lintrt rel du preprocessing rside principalement dans la compilation optionnelle. Ce mcanisme est fondamental lorsquun logiciel est maintenu sur plusieurs plateformes. Les zones de code dont linclusion dans le projet est fonction de la compilation doivent tre maintenues dans un fichier part. Cette difficult doit tre masque au dveloppeur dans toutes les autres parties du projet.
*** IMPRATIF ***

Les constantes de prprocessing dfinies automatiquement selon [Stroustrup 1991] sont : __LINE__ : valeur dcimale indiquant la ligne courante ; __FILE__ : chane de caractres indiquant le nom du fichier ;

__DATE__ : chane de caractres indiquant la date de la compilation du module courant selon le format suivant : Mmm dd yyyy ; __TIME__ : chane de caractres indiquant lheure de la compilation du module courante selon le format suivant : hh :mm :ss ; __cplusplus : simplement dfini pour indiquer que le compilateur attend du C++. Elle ne sont pas redfinissables directement (ni par #define, ni par #undef). Cependant la directive #line permet de redfinir __LINE__ et ventuellement __FILE__. Lusage de la directive #line nest pas recommand.

Timothe Royer (royer@uranus.crosat.fr)

version : 1997-02-25

page 37

Guide de dveloppement logiciel en C++


*** RECOMMANDATION ***

C++ coding guide

Les compilations optionnelles se dfinissent avec la commande de prprocessing #if, suivie ventuellement de defined(...) ou de !defined(...) .
*** RECOMMANDATION ***

Prfrer #if #ifdef/#ifndef. (Pourquoi ?) #if est plus gnral. Il permet au besoin de tester la valeur dune constante de prprocessing. (Exemple) Voici une implmentation dun code pouvant tre compil avec plusieurs niveaux de debug. Noter que la condition de compilation optionnelle est ajoute aprs le #endif qui termine la zone, pour faciliter la lisibilit et supprimer les ambiguts.
#if DEBUG_LEVEL > 0 // Quelques tests. #if DEBUG_LEVEL > 2 // D'autres tests. #endif // DEBUG_LEVEL > 2 #endif // DEBUG_LEVEL > 0
*** RECOMMANDATION ***

Faire suivre la commande de prprocessing #else par un commentaire contenant la condition du #if correspondant. Faire suivre la commande #endif de ce mme commentaire. Ajouter else si le test possde une clause else. (Exemple) Voici une option de compilation permettant de maintenir un code sur plusieurs versions dun compilateur :
#if COMPILER_VERSION < 2.7 // Oldies #else // COMPILER_VERSION < 2.7 // News #endif // else COMPILER_VERSION < 2.7

Voici un code utile pour permettre de simuler l'existence du type bool sur des compilateurs ne le proposant pas encore en standard :
#if !defined(__GNUG__) typedef int bool; const bool false = (0 == 1); const bool true = !false; #endif // !defined(__GNUG__)
*** RECOMMANDATION ***

Utiliser le mcanisme dassertion chaque fois quil est significatif. (Rappel) Linvariant est le fondement de la preuve de programme, science destine garantir le rsultat dun traitement de donnes. Il indique un ensemble de conditions qui doivent rester vraies tout au long de lexcution. Une assertion permet de vrifier un invariant de manire simple, dans un code source. (Pourquoi ?) Le mcanisme dassertion permet de dtecter un problme, ainsi que son identification lors de lexcution, autrement que par la constatation de ses effets. Il est facile dutilisation : sa syntaxe est concise et il est dbrayable facilement lorsque le code est test. De plus il facilite la maintenance et la relecture du code : il permet dexprimer linvariant du code et situe ainsi le contexte de validit des portions de code.

Timothe Royer (royer@uranus.crosat.fr)

version : 1997-02-25

page 38

Guide de dveloppement logiciel en C++

C++ coding guide

(Comment ?) Le assert est lun des rares mcanismes sains fond sur le prprocesseur. Son fonctionnement est simple. La macro ASSERT reoit une expression boolenne. Si elle est vraie, rien ne se passe. Si elle est fausse, alors le programme est devenu incohrent et ASSERT va indiquer un problme. FIXME assert.h Sous-chapitre ASSERT. Dans lidal, une mthode commence et se termine par un ASSERT() pour : Sassurer que la mthode se trouve dans un tat valide au moment ou on lapplique ; Sassurer que la mthode laisse la situation dans un tat valide. Cette technique est lapplication directe de la preuve de programme. Notons aussi que le mcanisme d'assertion permet d'afficher la date et l'heure de la dernire compilation du code source. Ceci pour identifier un problme d une dfinition incorrecte des dpendances dans le makefile qui aurait empch une recompilation ncessaire d'avoir lieu. Enfin, lorsqu'une assertion dtecte un problme, elle affiche un message puis produit un core (sous unix) qui permettra a posteriori de connatre l'tat de la mmoire ce moment l. Ces informations sont ensuite exploitables avec un debugger. (Exemple) Voir lannexe C - Exemple de prsentation pour une implmentation de ASSERT. Voici un exemple de scurisation de la fonction valeur absolue :
#include <limits.h> long Abs(const long thatValueToAbs) { ASSERT(thatValueToAbs != LONG_MIN); if(thatValueToAbs < 0) { return -thatValueToAbs; } else { return thatValueToAbs; } }

Rappel : les entiers signs sont cods sous le format complment deux. Or la valeur absolue de la plus grande valeur ngative est suprieure de 1 la plus grande valeur positive maintenable dans ce format, pour un nombre de bits donn. Ces valeurs limites dpendent de chaque machines et sont maintenues dans le header standard limits.h.
*** IMPRATIF ***

Lusage du prprocesseur doit tre limit au strict ncessaire. Lorsquune alternative se prsente, il faut toujours prfrer utiliser une fonctionnalit du langage C ou C++ une fonctionnalit du prprocesseur [Stroustrup 1995]. (Pourquoi?) Le prprocesseur est un parseur de texte qui ne respecte en aucune faon la signification du code, contrairement au compilateur. De plus les erreurs des au prprocesseur peuvent tre difficiles dtecter.
*** IMPRATIF ***

Dfinir une variable constante (sic) ou un enum plutt quune constante du prprocesseur. (Pourquoi ?) Les contrles sur les types, en C++, offrent une meilleure scurit sur des variables que sur des constantes du prprocesseur. (Exemple) Soit une fonction surcharge : Timothe Royer (royer@uranus.crosat.fr) version : 1997-02-25 page 39

Guide de dveloppement logiciel en C++


float cos(const float& thatAngle) ; double cos(const double& thatAngle) ; Laquelle est appele ? #define ANGLE1 123.456 #define ANGLE2 12345.6789 const double ANGLE3 = 112233.456 ;

C++ coding guide

(Exemple) Soit une constante :


#define MAX 50000

Le type de MAX sera diffrent suivant le compilateur utilis. Ce nest bien sr pas le cas de :
const int MAX = 50000;

De plus, un bon compilateur avertira le cas chant du dpassement de capacit de la valeur immdiate pour la constante quelle initialise, en fonction de son type.
*** IMPRATIF ***

Dfinir une fonction inline plutt quune macro. (Exemple) Voici un exemple dune fonction qui est traditionnellement dfinie comme une macro. Limplmentation prsente ici est aussi rapide lexcution, bnficie dun meilleur contrle sur les types, permet de mieux comprendre un problme ventuel lors de la compilation et permet de comparer des instructions complexes sans risque : elles ne sont values quune fois. Vrifier que, contrairement une dfinition de macro, cette fonction rsout Max(a,b++) sans effet de bord.
inline template<Type> const Type& Max(const Type& thatFirstArg2Compare, const Type& thatSecondArg2Compare) { if(thatFirstArg2Compare > thatSecondArg2Compare) { return thatFirstArg2Compare; } else { return thatSecondArg2Compare; } }
*** IMPRATIF ***

Limiter lusage de la commande #define des constantes qui seront utilises par le prprocesseur.

Debug
*** RECOMMANDATION ***

Le code source doit pouvoir compiler pour produire deux versions diffrentes : En version debug, incluant de nombreux tests permettant damliorer la qualit du code ; En version dfinitive (release), sans les tests. Cette version devra sexcuter rapidement, ne devra pas contenir dinformation de debug (qui permet entre autres

Timothe Royer (royer@uranus.crosat.fr)

version : 1997-02-25

page 40

Guide de dveloppement logiciel en C++

C++ coding guide

dditer le code source en entier, ce qui nest pas toujours souhaitable pour une version client). (Rappel) Il est cependant fondamental que les deux versions excutent le mme code, de la mme manire, aux tests prs : sinon la version debug nest plus reprsentative de version release. (Comment ?) Les zones de compilations optionnelles permettent d'implmenter ce mcanisme. (Exemple) Voici une mthode de classe String qui produit l'affichage de la chane qu'elle maintient. La classe dfinit deux donnes membres : int Size et char* Data. Elle vrifie simplement que la string se termine par le caractre null en version debug.
Void String::SelfDisplay(ostream& s) const { #if !defined(NO_DEBUG) int charCount; for(charC=0; charC<Size && Data[CharC]; CharC++) { } if(!Data[CharC]) { cerr << "Display invalid string." << endl; Data[Size-1] = 0; } #endif // !defined(NO_DEBUG) s << Data; }

Ce code peut tre compil de deux manires diffrentes : Avec vrification pour obtenir la version de travail :
cc String.cc -c -g3 +w -DNO_DEBUG

Sans vrification pour obtenir la version de livraison :


cc String.cc -c -O3 +w

La taille de l'excutable livrer pourra tre rduite grce la commande unix strip. Noter que, en version debug, le programme essaye de corriger le problme en fixant la fin de la chane null. Il pourrait aussi tre souhaitble de produire une image de la mmoire pour comprendre le problme au moyen de abort().
*** IMPRATIF ***

Les zones du code source destines seulement la dtection derreurs et qui sont dbrayables par une option de compilation ne doivent pas modifier les donnes du programme. (Pourquoi ?) Si le code destin au debug modifie des donnes, le programme sexcutera diffremment en version debug et en version release et celui-l ne sera plus significatif.
*** RECOMMANDATION ***

Par dfaut, le programme doit compiler en version debug. Une option de compilation dfinissant la constante NO_DEBUG permet de compiler en version release. (Comment ?) Tous les compilateurs C et C++ permettent de dfinir une constante de prprocessing lors de la compilation en passant sur la ligne de commande une option -D suivie du nom de la constante dfinir.

Timothe Royer (royer@uranus.crosat.fr)

version : 1997-02-25

page 41

Guide de dveloppement logiciel en C++

C++ coding guide

(Exemple) Voici un exemple de ligne de compilation permettant de compiler un fichier String.cc : Pour obtenir une version de test :
CC +w -o String String.cc

Pour obtenir une version release :


CC +w -o String -DNO_DEBUG String.cc
*** RECOMMANDATION ***

Une fonction doit commencer par une section dont la compilation est optionnelle, qui sert au debogage (debugging). (Pourquoi ?) Lors de la programmation dune fonction, il faut avoir le rflexe de la scuriser. Pour cela il faut que ses donnes de dpart soient valides. Tous les cas problmatiques ne peuvent pas toujours tre dtects. Cependant, certains indices doivent tre contrls, dans la mesure du possible (pointeurs null ou identiques, taille nulle...). Dans certains cas, un invariant de boucle ou de fin de procdure peut tre utile. Bien sr, la scurisation d'une mthode d'interface peut rester stricte, alors qu'une mthode interne pourra ventuellement s'excuter sans test en version release. (Exemple) Une fonction classique enfin scurise :
int Abs(const int& ThatValueToAbs) { #if !defined(NO_DEBUG) if(ThatValueToAbs == 1 << (sizeof(int) - 1)) { cerr << Abs() exception." << endl; } #endif // !defined(NO_DEBUG) if(ThatValueToAbs < 0) { return -ThatValueToAbs; } else { return ThatValueToAbs; } }

(Exemple) Voir le fichier exemple joint en annexe, XXX.cc pour un exemple de macro ASSERT.
*** AMLIORATION ***

Pour une plus grande efficacit du debug et pour bien contrler la mise au point et la livraison du logiciel, plusieurs niveaux de debug peuvent tre dfinis. (Exemple) Description des niveaux de debug : 0 : aucune scurit. Utilis uniquement comme indice de comparaison pour vrifier que le debug ne prend pas trop de temps lexcution. 1 : debug minimal, version destine tre livre. 2 : Version utilise lors de la mise au point du programme. 3 : Version contenant les tests coteux en temps. utiliser pour identifier un problme particulier. Timothe Royer (royer@uranus.crosat.fr) version : 1997-02-25 page 42

Guide de dveloppement logiciel en C++


*** AMLIORATION ***

C++ coding guide

La convention de nommage des paramtres diffre de celle des variables locales. (Pourquoi ?) Lors de la maintenance, modifier le type dune donne, na pas les mmes consquences sur une variable que sur un paramtre. lusage, il est trs pratique de les diffrencier instantanment. Bien sr, une autre mthode pour le faire peut tre choisie. (Exemple) Le nom dun paramtre commence toujours par that ou bien par un caractre de soulignement _. Les autres mots jamais. (exception : les arguments de : :main(argc,argv) sont admis par tous et doivent tre utiliss).

Instruction
Les oprateurs unaires ++ etdoivent prcder la variable quils modifient. (Pourquoi ?)FIXME.
*** RECOMMANDATION ***

Les oprateurs unaires ++ etdoivent tre utiliss seuls. (Exemple) Voici des cas o le comportement du code est indtermin. Les problmes illustrs dans ces exemples ne seraient pas apparus si les oprateurs unaires dincrmentation ou de dcrmentation avaient t utiliss seuls, dans une instruction C++ distincte.
int a = 666; int b; b = a++; a += --a;
*** RECOMMANDATION ***

Une instruction ne doit pas contenir plusieurs affectations (oprateurs =, +=, -=, *=, /=) ; (Exemple) viter par exemple :
int a = 3; int b = 2; a -= b /= 3;
*** RECOMMANDATION ***

viter dutiliser loprateur ternaire ? :. Utiliser plutt une structure de type "if". (Pourquoi ?) Loprateur ternaire ? : gne la lisibilit du code.
*** RECOMMANDATION ***

Une valeur numrique ne doit pas apparatre en clair dans un .cc (sauf 0). Si son usage est ncessaire, elle doit tre affecte une variable constante dclare extern dans le .hh et dfinie dans le .cc correspondant.
*** IMPRATIF ***

Lorsque de nombreuses valeurs numriques doivent, tre dfinies, il faut qu'elles le soient dans un fichier texte ASCII lu lors de l'excution.
*** AMLIORATION ***

Lusage dune valeur numrique immdiate (diffrente de 0, true ou false) dans le source est la plupart du temps rvlatrice dune difficult de conception. Timothe Royer (royer@uranus.crosat.fr) version : 1997-02-25 page 43

Guide de dveloppement logiciel en C++


*** IMPRATIF ***

C++ coding guide

Utiliser les oprateurs new et delete pour grer dynamiquement la mmoire. Ne pas utiliser malloc, realloc... (Exemple) new et delete respectent mieux labstraction des donnes et offrent les mmes possibilits que malloc. new revoie un pointeur typ, contrairement alloc. new permet lappel au constructeur.
*** RECOMMANDATION ***

Utiliser typedef pour manipuler des types de donnes non lmentaires. (Pourquoi ?) Dclarer un tableau de pointeurs sur fonction sans utiliser typedef .
*** RECOMMANDATION ***

Une instance ou une mthode ne doit pas tre dclare extern plus dune fois. Elle est dclare, au besoin, dans le fichier dentte ( .hh ) associ au fichier de dclaration ( .cc ) contenant la dclaration relle. (Pourquoi?) FIXME
*** RECOMMANDATION ***

Le corps des fonctions inline est dfini dans le fichier dentte. (Pourquoi ?) Le corps dune fonction doit tre disponible lorsquil est insr.
*** AMLIORATION ***

Les templates doivent tre dfinis inline. (Pourquoi ?) Le corps dun template doit tre disponible lorsquil est fix pour un type particulier. Contrairement ce que cette architecture suggre, les templates ne sont pas implmentes comme une interprtation , mais elles sont instancies pour chaque type ncessaire. Ceci permet entre autres dutiliser facilement lditeur de liens standard.

Timothe Royer (royer@uranus.crosat.fr)

version : 1997-02-25

page 44

Guide de dveloppement logiciel en C++

C++ coding guide

Chapitre 4 - Algorithmique
Dans ce chapitre, nous allons prsenter les recommandations les plus abstraites de ce guide. Elles concernent les choix algorithmiques motivant limplmentation. FIXME functer class
*** IMPRATIF ***

Ne jamais enchaner plusieurs drfrencement de donnes membres : (Exemple) Voil o conduit un respect approximatif du modle objet (J'ai dj trouv dans un projet comercialis une srie de 6 drfrencements comme ceux-ci...) :
titi.toto().->tata.tutu = 255;
*** IMPRATIF ***

Const doit tre utilis chaque fois que possible. (Pourquoi ?) Lindication const permet de sassurer quune donne ne sera pas modifie, mme si elle est passe comme paramtre une fonction dont le corps nest pas connu ou si un pointeur sur son adresse est dfini.
*** IMPRATIF ***

Toujours vrifier la valeur de retour dun appel systme. (Comment ?) Dans un code bien encapsul, le nombre de ces appels doit tre rduit et ce contrle ne devrait pas tre fastidieux mettre en oeuvre.
*** IMPRATIF ***

Lorsquun oprateur = est dfini, sassurer quune instance peut tre affecte elle-mme. (Pourquoi ?) Ceci demande un traitement particulier, surtout lorsque de la mmoire doit tre libre ou alloue dynamiquement lors dune affectation. (Exemple) Voici un exemple doprateur = redfini qui fonctionne dans tous les cas sauf lorsquune instance de cette classe est affecte elle-mme :
class String { char* Data; long Size; // ... public : String& operator =(const String& _newValue) { delete[] Data; Size = _newValue.Size; Data = new char[ Size]; strcpy(Data,_newValue.Data); return *this; } };

Timothe Royer (royer@uranus.crosat.fr)

version : 1997-02-25

page 45

Guide de dveloppement logiciel en C++

C++ coding guide

Si une instance de la classe String dfinie ci-dessus est affecte elle-mme, alors sa zone de donne est dabord ralloue puis lue ensuite. Un contrle de ce type permettrait de rsoudre ce problme :
if(this == &_newValue) { return *this; } else // ...
*** IMPRATIF ***

Ne jamais imposer une limite arbitraire dimplmentation. (Exemple) Un des exemples les plus reprsentatifs est la limite de la commande un*x tar qui ne permet pas de manipuler des path de plus de 100 caractres. Ne pas crire de fonctions qui prsentent de telles limitations.
*** AMLIORATION ***

Linterface des classes doit tre minimale. (Comment ?) Le nombre de mthodes de chaque classe doit tre minimal. Le nombre de paramtres que reoit chaque mthode doit tre minimal.
*** RECOMMANDATION ***

Lorsquune fonction dtecte une erreur, elle doit la traiter et non pas simplement retourner un code derreur. (Pourquoi ?) Lexprience a montr que les programmeurs testaient rarement les valeurs de retour des appels systmes indiquant si ceux-ci se sont bien passs. (Comment ?) Il faut adopter une dmarche diffrente en concevant une interface : lorsquune mthode dtecte un problme qui ne doit pas arriver, elle doit lindiquer elle-mme, au moins en mode debug. Il nest pas suffisant de renvoyer un code derreur.
*** AMLIORATION ***

Lorsquune classe fonctionne de manire trs dynamique, il peut tre intressant de garantir quelle a bien t alloue et initialise chaque utilisation. (Comment ?) Un champ (de type long par exemple) maintient ltat de linstance : alloue, initialise et desalloue. (Exemple) Une mthode simple pour utiliser ce systme pour une classe est dhriter de la classe Cookie mise en annexe. Lappel la classe Cookie : :OK() valide lexistence de linstance. Cet hritage peut tre conditionn par NO_DEBUG.
*** RECOMMANDATION ***

Ne pas passer plus de quatre paramtres. (Pourquoi ?) Lexistence de fonctions contenant un trop grand nombre de paramtres indique un problme de conception. Elle favorise lapparition derreurs lors de son utilisation (ordre des paramtres), augmente le couplage des interfaces, gne la relecture et la maintenance du code source.

Timothe Royer (royer@uranus.crosat.fr)

version : 1997-02-25

page 46

Guide de dveloppement logiciel en C++


*** RECOMMANDATION ***

C++ coding guide

Ne jamais traiter directement la mmoire laide dun pointeur. En particulier, ne jamais utiliser memcpy, ni aucune autre fonction mem*. (Pourquoi ?) Ces fonctions brisent le modle objet sans apporter de possibilits supplmentaires. (Exemple) Si une instance est recopie avec memcpy et quun oprateur = est dfini par la suite pour sa classe, celui-ci ne sera pas utilis. Il laurait t si loprateur = gnr par dfaut par le compilateur avait t utilis.
*** AMLIORATION ***

Ne pas dfinir de fonction dont certains paramtres ont une valeur par dfaut. (Pourquoi ?) En pratique, cela conduit des appels de fonctions avec un nombre darguments variable. Cette souplesse apparente que confre au premier abord une telle interface se traduit plutt par une confusion lors de l'utilisation de la classe. (Exemple) Cette dfinition de construction de Point est -elle souhaitable ?
class Point { Point(int x=0, int y=0, int z=0); } main() { Point Point Point Point }

a; b(1); c(5,6); d(6,6,6);

Celle-ci, plus stricte semble prferable :


class Point { Point(void); Point(int x, int y, int z); } main() { Point a; // Point b(1); // interdit // Point c(5,6); // interdit Point d(6,6,6); }
*** AMLIORATION ***

Eviter d'inserrer des donnes enum, #define ou global const int. (Pourquoi ?) Gnre trop d'inclusions des headers. FIXME.

Gestion dun source maintenu pour plusieurs plateformes.


*** IMPRATIF ***

Dater lanne sur 4 chiffres. Timothe Royer (royer@uranus.crosat.fr) version : 1997-02-25 page 47

Guide de dveloppement logiciel en C++

C++ coding guide

(Pourquoi ?) Dans beaucoup de programmes la date est code sur deux chiffres. Ils ne rsisteront pas au passage lan 2000. Le lundi 3 janvier 2000 sera le lundi noir de linformatique. Les visionnaires coderont l'anne des dates de leurs programmes sur 5 chiffres ou plus. propos, noter que lan 2000 sera la dernire anne du vingtime sicle.
*** AMLIORATION ***

Utiliser le format japonais pour indiquer une date : aaaa/mm/jj. (Pourquoi ?) Ce format limine les ambiguts entre le format franais et le format anglosaxon. De plus, le tri alphabtique de dates codes ainsi correspond au tri chronologique. (Exemple) En gardant la logique actuelle, les dates vont bientt scrire 03/07/02. Sagit-il du 3 juillet ou du 7 mars de lan 2 ? Prfrer : 2002/03/07.
*** RECOMMANDATION ***

Eviter les casts (transtypage/coercition), en particulier sils sont implicites. (Pourquoi?) FIXME (Exemple) FIXME
*** IMPRATIF ***

Ne jamais transtyper une donne const en non-const. (Pourquoi ?) Selon limplmentation, il est possible que les donnes const soient stockes dans une zone o la lecture est possible, mais pas lcriture. De plus ce type de transtypage indique une mauvaise matrise de la structure de donnes. (Comment ?) Si une donne membre doit pouvoir tre modifie dans une instance const, utiliser le mot clef mutable sa dclaration.
*** RECOMMANDATION ***

Toutes les occurences dune fonction surcharge doivent remplir le mme objectif. Leur intrt doit se limiter au masquage de difficults dimplmentation. (Pourquoi ?) Il est possible de faire coexister deux fonctions qui ont le mme nom et qui sont diffrencies par le type du paramtre reu. Il nest pas souhaitable que ces fonctions remplissent des buts trs (ou, pire, lgrement) diffrents. La surcharge de fonctions est un art difficile manier avec parcimonie et clairvoyance pour rester bnfique.
*** RECOMMANDATION ***

Prfrer les multiplications et divisions aux dcalages de bits. (Exemple) En particulier, leffet dun dcalage droite pour une valeur ngative nest pas dfini [Stroustrup 1991].
*** RECOMMANDATION ***

Un pointeur doit toujours pouvoir tre dsallou. (Pourquoi ?) Ceci vite une gestion de flags lourde et gnratrice derreurs. Pour remplir cet objectif, un pointeur doit, soit dsigner une zone alloue, soit tre nul. En effet les oprateurs delete et delete[] nont pas deffet sils reoivent un pointeur nul.

Timothe Royer (royer@uranus.crosat.fr)

version : 1997-02-25

page 48

Guide de dveloppement logiciel en C++

C++ coding guide

(Rappel) Loprateur delete est utilis pour dsallouer une zone rserve pour une instance par loprateur new. Loprateur delete[] est utilis pour dsallouer une zone rserve pour un tableau dinstances par loprateur new[]. (Exception) La seule exception cette rgle concerne les tableaux cods en dur. (char buff[64] ;) Bien sr il faut leur usage prfrer un template comme ceux des STL.
*** RECOMMANDATION ***

Un pointeur sur char rfrenant une chane de caractres doit toujours dsigner une zone alloue et valide. (Pourquoi ?) FIXME
*** RECOMMANDATION ***

Ne rien supposer de la structure interne des donnes. Dune manire gnrale, ne jamais briser labstraction des donnes. (Pourquoi ?) En particulier, les compilateurs peuvent rarranger lordre des champs dune struct dans un souci doptimisation. Le code doit toujours tre compltement indpendant de ces dtails dimplmentation [Stroustrup ? ?].
*** RECOMMANDATION ***

Identifier clairement chaque rcursivit. (Pourquoi ?) Cette algorithmique nest pas courante en C++. Lors de la maintenance, un lecteur non averti pourra avoir des difficults comprendre larbre dappel des fonctions si une rcursivit a t utilise, surtout si elle est indirecte et quelle nest pas explicitement indique.
*** RECOMMANDATION ***

Ne pas dclarer de donnes static dans une fonction. (Pourquoi?) FIXME


*** RECOMMANDATION ***

Il est la charge de celui qui alloue de la mmoire de la dsallouer. (Exemple) Comment implmenter une fonction qui renvoie un message sous forme de chane de caractres ? La solution simple consiste allouer dynamiquement un tableau de caractres, le remplir avec le message et renvoyer un pointeur sur la chane. Cependant, cest alors celui qui reoit le message qui doit dtruire la zone alloue une fois que la donne nest plus ncessaire. Ce type de conception conduit facilement une fuite despace mmoire (memory leak). Voici une meilleure solution ce problme :
class TmpString { char* Data; public: TmpString(const char* const thatClone) { ... } TmpString(const TmpString& thatClone) { ... } operator char*(void) { return Data; } }; TmpString ClearErrorMessage(const long& thatErrorcode) { if(thatErrorCode == 1) return "real bad"; else return "not too bad"; }

Timothe Royer (royer@uranus.crosat.fr)

version : 1997-02-25

page 49

Guide de dveloppement logiciel en C++

C++ coding guide

int main(void) { // ... char* Buff = new char[strlen(ClearErrorMessage(CurrentError))]; strcpy(Buff,ClearErrorMessage(CurrentError)); cout << "Error: " << ClearErrorMessage(CurrentError) << endl; return 0; }

FIXME exemple dbile

Pointeurs
*** RECOMMANDATION ***

Il ne doit pas y avoir de pointeur dans le programme, mis part dans quelques classes de base. (Pourquoi ?) Les pointeurs invalides sont sans doute la plus grande cause derreurs des programmes crits en C. Ils gnent labstraction du code source et obligent le dveloppeur maintenir lesprit une notion simplement technique. Lorientation objet permet dviter ces inconvnients. (Comment ?) Lutilisation des trois classes suivantes permet de programmer sans jamais utiliser de pointeur ni dallocation dynamique directement : (bien sr, ceci ne sapplique pas aux cas particuliers comme la programmation systme bas niveau) String pour grer les chanes de caractres ; Vector pour grer les tableaux ; List pour grer les listes chanes. Ces classes font partie de la STL (Standard Template Library) qui ont t normalises fin 1995 avec le C++. J'ai personnellement crit plusieurs projets de + de 10.000 lignes qui ne contenaient des pointeurs que dans ces classes de base, dment testes.

Structures de contrle
*** RECOMMANDATION ***

Ne pas utiliser plus de quatre niveaux dimbrication daccolades.


*** AMLIORATION ***

Respecter les rgles de la programmation structure : viter les mots-clefs : break ; continue ; goto ; viter aussi lemploi de plusieurs return dans une fonction. (Exception) Si un switch doit tre utilis, alors un "break" doit terminer chaque structure "case". Timothe Royer (royer@uranus.crosat.fr) version : 1997-02-25 page 50

Guide de dveloppement logiciel en C++


*** AMLIORATION ***

C++ coding guide

Lusage de switch nest pas recommand dune manire gnrale. Certaines exceptions comme lidentification de touches subsistent cependant. (Pourquoi ?) La structure logique du switch repose directement sur le goto et les labels et ne respecte pas lide admise de la programmation structure. Limplmentation, cause des break en particulier, est une source derreurs. (Comment ?) Un algorithme d'excution conditionnelle fond sur un tableau de pointeur sur fonctions est plus volutif, plus propre, moins sujets aux difficults de maintenance. Accessoirement, son excution est aussi beaucoup plus rapide qu'un switch. Pour les cas simples, une structure fonde sur le test else if peut permettre de grer proprement une succession de tests :
if(key == ENTER_KEY) // ... else if(key == F1_KEY) // ... else if((key >= 'a') && (key <= 'z')) // ... else // Erreur...

(Exemple) Par exemple, il est possible quun polymorphisme prenne en charge de manire transparente une difficult rsolue par un switch. // FIXME kezako
*** IMPRATIF ***

Si une structure switch doit tre utilise, un break doit terminer chaque clause case .
*** RECOMMANDATION ***

Chaque switch doit se terminer par un label default .


*** AMLIORATION ***

Lusage de do {...} while(...) est inhabituel et nest pas souhaitable.


*** AMLIORATION ***

Restreindre lusage de la boucle for une itration de 0 une valeur fixe, avec une incrmentation de 1. (Pourquoi ?) Ce genre de choix conduit le code tre self-explanatory. Aux dpens parfois de la satisfaction intellectuelle du dveloppeur. (Exemple)
for(int i = 0; i < size; i++) { cout.width(3); cout << i << ' ' << char(i) << endl; }
*** RECOMMANDATION ***

Lorsquun oprateur doit tre surcharg et que son premier argument est constant, le dfinir comme une fonction et non comme une mthode. (Pourquoi ?) La dfinition dun oprateur comme mthode et non comme fonction ne permet pas le cast automatique du premier oprande par appel au constructeur, lors de lutilisation de

Timothe Royer (royer@uranus.crosat.fr)

version : 1997-02-25

page 51

Guide de dveloppement logiciel en C++

C++ coding guide

loprateur. Cependant, ceci prsente un intret lorsque le premier oprande est une rvalue (dhabitude, comme pour + ou ==, mais pas comme = ou +=). (Exemple) Comparaison des deux techniques pour une classe String :
class String { char* Data; public: String(void) { Data = new char[1]; Data[0] = '\0'; } String(const long& thatLength) { Data = new char[thatLength+1]; Data[0] = '\0'; } String(char* const thatClone) { Data=new char[strln(thatClone+1)] strcpy(Data,thatClone); } #if defined OPERATOR_IS_METHOD Integer operator +(const Integer& thatSecondOp erand) const { String returnValue(newchar [strln(Value)+strln(thatSecondOperand)+1]); strcpy(returnValue.Data,Data); strcpy(returnValue.Data,thatSecondOperand); return returnValue; } #endif // defined OPERATOR_IS_METHOD Integer }

FIXME exemple trop pourri

?. Optimisation
*** AMLIORATION ***

Loptimisation est un art difficile. Une horreur de programmation est souvent justifie par un besoin doptimisation. Lexprience montre que loptimisation ne peut se faire efficacement sans profiler. Dune manire gnrale, trs peu de fonctions occupent plus de 1% du temps de calcul. Si le programme doit rellement tre optimis, il faut identifier ces fonctions. Dune manire gnrale, il faut donc respecter le modle implment, mme au prix de quelques cycles de processeur.
*** AMLIORATION ***

Une portion de source rpte plus de deux fois, mme avec de lgres diffrences, indique une mauvaise conception.
*** AMLIORATION ***

Affiner le modle objet plutt quutiliser des ruses de programmation pour optimiser globalement lexcution. (Exemple) Voir Limplmentation du noeud de liste doublement chane mise en annexe. La place quoccupe cette gestion de liste en mmoire est minimale. La taille du code aussi. Enfin, le temps dexcution est proche de loptimum : le code ne contient aucun branchement conditionnel (if, ? :) et aucune boucle. De plus, il est inline. Ceci sans compromis par rapport au modle objet et sans technique obscure doptimisation. Timothe Royer (royer@uranus.crosat.fr) version : 1997-02-25 page 52

Guide de dveloppement logiciel en C++

C++ coding guide

?. Dveloppement multi-plateformes
*** IMPRATIF ***

Isoler le code source contenant des parties dont la compilation est optionnelle pour des besoins de portages. Ces fichiers doivent contenir tout ce code et uniquement ce code.

Timothe Royer (royer@uranus.crosat.fr)

version : 1997-02-25

page 53

Guide de dveloppement logiciel en C++

C++ coding guide

Chapitre 5 - Mise en uvre


"La diffrence entre la thorie et la pratique est plus importante en pratique qu'en thorie." Sagesse populaire logicienne. "C'est pratiquement vrai. En thorie, du moins." Robert. "Et rciproquement." Lulu.

Compilation
*** IMPRATIF ***

Il faut compiler en demandant au compilateur dindiquer tous les avertissements (warnings) et toutes les erreurs. Il faut les viter tous, sauf lorsque le message est li une limitation du compilateur. (Exemple) Voici par exemple les options de compilation passer g++ pour quil indique tous les warnings. Les deux derniers ne sont pas utilisables avec iostream.h de la libg++ 2.7.0 : g++ : -Wall -Wpointer-arith -Wbad-function-cast -Wcast-qual -Wcast-align -Wwrite-strings Wconversion -Wstrict-prototypes -Wmissing-prototypes -Wmissing-declarations -Wnestedexterns -Winline -Wsynth -Wredundant-decl -Wshadow Pour cfront et ses descendants plus directs, loption +w permet gnralement d'obtenir tous les avertissements.
*** IMPRATIF ***

Une classe ayant une mthode virtuelle doit avoir un destructeur virtuel. (Pourquoi ?) La dfinition de mthodes virtuelles sert implmenter le polymorphisme sur les classes de base. lutilisation, un pointeur sur classe de base peut en fait dsigner une instance de classe drive. Lors de la destruction de cet objet, pour que le bon destructeur soit appel, il faut que ceux des classes de base soient virtuels.
*** AMLIORATION ***

Utiliser plusieurs compilateurs pour compiler le mme code. (Pourquoi ?) Alors que les compilateurs C sont stabiliss et fiables depuis plusieurs annes, les compilateurs C++ ne le sont pas. Ils peuvent en particulier laisser passer une instruction invalide. Ils peuvent aussi gnrer un code invalide (cf. exemple). Compiler un excutable avec plusieurs compilateurs/sur plusieurs machines est implicitement un moyen de valider la portabilit et la modularit du code.

Timothe Royer (royer@uranus.crosat.fr)

version : 1997-02-25

page 54

Guide de dveloppement logiciel en C++


*** AMLIORATION ***

C++ coding guide

Il est interdit daffecter un tableau dintances un pointeur sur classe abstraite dont hrite la classe de ces instances. A lexcution, le compilateur ne trouve pas la table dindirection. Attention : le compilateur ne prvient pas lorsque ce problme arrive. (Exemple) En testant 4 compilateurs sur 3 machines diffrentes (CC sun ancien, gcc 2.7.0, CC silicon 4 et 5 et borland) tous compilaient le code suivant sans warning, mais aucun ne produisait dexcutable utilisable !
#include <iostream.h> class B {virtual ~B() {}}; class D:public B {int i; D(){i=1;} virtual ~D(){cout<<i<<endl;}}; int main(void) { B* pb = new D[2]; delete[] pb; return 0; }
*** RECOMMANDADTION ***

Utiliser des outils de scurisation du code. Le C++ est un outil dimplmentation puissant, mais o une simple faute dattention peut engendrer une erreur alatoire trs difficile localiser. Pour contrer cette situation, il faut appliquer des outils de vrification qui se rvlent trs performants lusage. (Exemple) lint++ : vrification du code source plus pousse que celle du compilateur. Purify : vrification de lutilisation de la mmoire durant lexcution.

Timothe Royer (royer@uranus.crosat.fr)

version : 1997-02-25

page 55

Guide de dveloppement logiciel en C++

C++ coding guide

Bibliographie
[Dijkstra 1972] Structured programming O-J Dahl, E. W. Dijkstra, C. A. R. Hoare 220 pages. Anglais. Academic press. 11me dition. ISBN 0-12-200550 FIXME [Ellemtel 1992] Programming in C++. Rules and Recommandations. Ellemtel corporation erik.nyquist@eua.ericsson.se mats.henric@eua.ericsson.se 88 pages. Anglais. Document assez complet, dont le but est proche de celui-ci. Il sadresse des programmeurs plus spcialiss. Certaines prconisations sont proches de celles de ce document sous de nombreux aspects. Comprend moins de rgles et plus d'exemples. [Stroustrup 1991] The C++ programming language. 2nd edition. Corrections 1995. Bjarne Stroustrup 699 pages. En anglais amricain. Addison Wesley Publishing Company ISBN 0-201-53992-6 699 pages Seconde dition de la rfrence du C++. Unique ouvrage utiliser pour apprendre le C++. Cependant, quelques ajouts au langage manquent (exceptions, type bool). [Stroustrup 1992] The Annotated C++ Reference Manual Margaret A. Ellis - Bjarne Stroustrup Addison Wesley Publishing Company ISBN 0-201-51459-1 461 pages [Stroustrup 1995] The design and evolution of C++ Bjarne Stroustrup Addison Wesley Publishing Company ISBN 0-201-54330-3 461 pages [Code complete 19??] Timothe Royer (royer@uranus.crosat.fr) version : 1997-02-25 page 56

Guide de dveloppement logiciel en C++ Code Complete Steve McConnel Microsoft Press ISBN 1-55615-484-4 859 pages C++ FAQ usenet : comp.lang.c++ [Adams 1978] The Hitch Hiker's guide to the galaxy Douglas Adams ISBN 0-330-25864-8 159 pages Gnu recommandations Meyer Characteristics of Sotware Quality, Boehm et al.

C++ coding guide

Timothe Royer (royer@uranus.crosat.fr)

version : 1997-02-25

page 57

Guide de dveloppement logiciel en C++

C++ coding guide

Annexe A - Application du guide au code c


Paradoxalement, la programmation en C selon les standards efficaces algorithmiques unanimement reconnus demande une plus grande matrise de lorientation objet et de la syntaxe que la programmation en C++. Dans certains cas, prfrer le C au C++ est comprhensible : besoin de portabilit sur des plateformes possdant un compilateur C, mais pas de compilateur C++ (autre raison ?). Tous les mcanismes prsents ici peuvent tre ports au C au prix dun effort plus ou moins important. En fait, cet effort est raisonnable dans la plupart des cas. Lexemple fondamental est limplmentation du mcanisme de classe. Voici un exemple : une struct String. FIXME Data length.
struct String { char* Data; }; void StringVoidConstructor(String thatConstructedString) { thatConstructed String.Data = malloc(sizeof(char)); thatConstructedString.Data[0] = '\0'; } void StringCopyConstructor(String thatClone) { thatConstructedString.Data = malloc(strlen(thatClone.Data)+1); strcpy(thatConstructedString.Data,thatclone.Data); } void StringCharStarConstructor(char* thatPseudoClone) { thatConstructedString.Data = malloc(strlen(thatPseudoClone)+1); strcpy(thatConstructedString.Data,thatPseudoClone); } void StringDestructor(String thatStringToDestroy) { free(ThatStringToDestroy.Data); } String Concat(String thatStart, String thatEnd) { String ReturnValue; ReturnValue.Data = malloc(strlen(thatStart.Data)+strlen(thatEnd.Data)+1; ReturnValue.Data = strcpy(thatStart.Data); ReturnValue.Data = strcat(thatEnd.Data); return ReturnValue; } char* CharStarCast(String thatStringToCast) { return thatStringToCast.Data; }

Timothe Royer (royer@uranus.crosat.fr)

version : 1997-02-25

page 58

Guide de dveloppement logiciel en C++ Du code source en c et en c++ peuvent crer un excutable.

C++ coding guide

Les diffrents fichiers objets crs (*.o) peuvent tre lis entre eux lors du link. (Exemple) Voici un exemple minimal dutilisation mutuelle de c et de c++. Voici une classe dont une mthode appelle une fonction c :
#include <iostream.h> extern "C" { int IAmACFunction(int i); void InstanciateAA(void); ThisCFunctionInstanciateAnObject(); } class A { public: A(void) { cout << "A born" << endl; } }; void InstanciateAA(void) { new A; } int main(void) { IAmACFunction(666); ThisCFunctionInstanciateAnObject(); return 0; }

Voici une fonction c qui provoque la construction d un objet c++ en appelant une fonction dfinie dans le code c++ :
#include <stdio.h> extern InstanciateAA(); int IAmACFunction(i) int i; { printf("IAmACFunction that received } ThisCFunctionInstanciateAnObject() { InstanciateAA() ; }

: %d\n ,i) ;

Timothe Royer (royer@uranus.crosat.fr)

version : 1997-02-25

page 59

Guide de dveloppement logiciel en C++

C++ coding guide

Annexe B - Comment lier des modules C et C++ au sein d'un mme excutable
FIXME explications

Timothe Royer (royer@uranus.crosat.fr)

version : 1997-02-25

page 60

Guide de dveloppement logiciel en C++

C++ coding guide

############################################################################### # # File : Makefile # # Birth : 1996/08/13 # # Version : 1996/08/13 # # Purpose : To show how to mix C and C++ files. # # Author : Timothee Royer # ############################################################################### CC=cc CXX=g++ CFLAGS=+w CXXFLAGS=-Wall CXX_STD_INCLUDES=-I I/usr/include SRCS=titi.c toto.cc OBJS=titi.o toto.o EXEC_NAME=tata

/usr/include/CC

-I

/opt/outils/gnu/lib/g++-include

############################################################################### $(EXEC_NAME) : $(OBJS) $(CXX) -o $@ $(OBJS) depend : makedepend $(CXX_STD_INCLUDES) -- $(SRCS) clean : rm -f *.o core $(OBJS) $(EXEC_NAME) # DO NOT DELETE THIS LINE -- make depend depends on it. titi.o: toto.o: toto.o: toto.o: toto.o: /usr/include/stdio.h titi.h /opt/outils/gnu/lib/g++-include/iostream.h /opt/outils/gnu/lib/g++-include/streambuf.h /opt/outils/gnu/lib/g++-include/libio.h /opt/outils/gnu/lib/g++-include/_G_config.h toto.hh titi.h

Timothe Royer (royer@uranus.crosat.fr)

version : 1997-02-25

page 61

Guide de dveloppement logiciel en C++

C++ coding guide

/****************************************************************************** ** ** File : titi.h ** ** Birth : 1996/08/13 ** ** Version : 1996/08/13 ** ** Purpose : To show how to mix C and C++ files. ** ** Author : Timothee Royer ** ******************************************************************************/ #if defined(titi_RECURSES) #error Recursive header files inclusion detected in titi.h #else /* defined(titi_RECURSES) */ #define titi_RECURSES #if !defined titi_hh #define titi_hh /*****************************************************************************/ #if defined(__cplusplus) extern "C" { #endif /* defined(__cplusplus) */ void InstanciateAA(void); void IAmACFunction(int i); void ThisCFunctionInstanciateAnObject(void); #if defined(__cplusplus) } #endif /* defined(__cplusplus) */ /*****************************************************************************/ #endif /* !defined titi_hh */ #undef titi_RECURSES #endif /* esle defined(titi_RECURSES) */

Timothe Royer (royer@uranus.crosat.fr)

version : 1997-02-25

page 62

Guide de dveloppement logiciel en C++

C++ coding guide

/****************************************************************************** ** ** File : titi.h ** ** Birth : 1996/08/13 ** ** Version : 1996/08/13 ** ** Purpose : To show how to mix C and C++ files. ** ** Author : Timothee Royer ** ******************************************************************************/ #include <stdio.h> #include "titi.h" void IAmACFunction(i) int i; { printf("IAmACFunction that received : %d\n",i); } void ThisCFunctionInstanciateAnObject() { InstanciateAA(); }

Timothe Royer (royer@uranus.crosat.fr)

version : 1997-02-25

page 63

Guide de dveloppement logiciel en C++

C++ coding guide

/////////////////////////////////////////////////////////////////////////////// // // File : toto.hh // // Birth : 1995/08/21 // // Version : 1996/08/13 // // Purpose : To show how to mix C and C++ files. // // Author : Timothee Royer // /////////////////////////////////////////////////////////////////////////////// #if defined(toto_RECURSES) #error Recursive header files inclusion detected in toto.hh #else // defined(toto_RECURSES) #define toto_RECURSES #if !defined toto_hh #define toto_hh /////////////////////////////////////////////////////////////////////////////// extern "C" { void InstanciateAA(void); } class A { public: A(void); }; /////////////////////////////////////////////////////////////////////////////// #endif // !defined toto_hh #undef toto_RECURSES #endif // esle defined(toto_RECURSES)

Timothe Royer (royer@uranus.crosat.fr)

version : 1997-02-25

page 64

Guide de dveloppement logiciel en C++

C++ coding guide

/////////////////////////////////////////////////////////////////////////////// // // File : toto.cc // // Birth : 1995/08/21 // // Version : 1996/08/13 // // Purpose : To show how to mix C and C++ files. // // Author : Timothee Royer // /////////////////////////////////////////////////////////////////////////////// #include <iostream.h> #include "toto.hh" #include "titi.h" /////////////////////////////////////////////////////////////////////////////// A::A(void) { cout << "A born." << endl; } void InstanciateAA(void) { A someA; } /////////////////////////////////////////////////////////////////////////////// int main(void) { IAmACFunction(666); ThisCFunctionInstanciateAnObject(); return 0; } ///////////////////////////////////////////////////////////////////////////////

Timothe Royer (royer@uranus.crosat.fr)

version : 1997-02-25

page 65

Guide de dveloppement logiciel en C++

C++ coding guide

Annexe C - Modle de fichiers sources


Voir les fichiers XXX.hh et XXX.cc joints. Ils contiennent des fonds de classe. Pour les utiliser, il est recommand de les recopier dans un rpertoire o lon souhaite crire une nouvelle classe. Remplacer les XXX par le nom de la classe. Remplir les champs de commentaire. Ajouter des mthodes au besoin.

Timothe Royer (royer@uranus.crosat.fr)

version : 1997-02-25

page 66

Guide de dveloppement logiciel en C++

C++ coding guide

/////////////////////////////////////////////////////////////// // // // File name : XXX.hh // // Creation : 19??/??/?? // // Version : 19?? /??/?? // // Author : ?? // // History : // 19??/??/?? : Mr ?Name? : ?What? // // Rcs Id : "@(#)class XXX declaration." // // /////////////////////////////////////////////////////////////// #if defined XXX_CYCLE #error Header cyclic inclusion detected in XXX.hh #else // defined XXX_CYCLE #define XXX_CYCLE #if !defined XXX_hh #define XXX_hh /////////////////////////////////////////////////////////////// // // #include <iostream.h> /////////////////////////////////////////////////////////////// // class XXX /////////////////////////////////////////////////////////////// class XXX { public : // Standard services ~XXX(void); // Interface void SelfDisplay(ostream& thatStream) const; bool OK(void) const; private : // Datas // Hidden services inline XXX(void); inline XXX(XXX&); inline operator =(XXX&); // Internals }; inline ostream& operator <<(ostream& thatStream, const XXX& thatObjectToDisplay);

Timothe Royer (royer@uranus.crosat.fr)

version : 1997-02-25

page 67

Guide de dveloppement logiciel en C++


ostream& operator <<(ostream& thatStream, const XXX& thatObjectToDisplay) { thatObjectToDisplay.SelfDisplay(thatStream); return thatStream; }

C++ coding guide

// // /////////////////////////////////////////////////////////////// #endif // !defined XXX_hh #undef XXX_CYCLE #endif // else defined XXX_CYCLE

Timothe Royer (royer@uranus.crosat.fr)

version : 1997-02-25

page 68

Guide de dveloppement logiciel en C++

C++ coding guide

/////////////////////////////////////////////////////////////// // // // File name : XXX.cc // // Creation : 19??/??/?? // // Version : 19??/??/?? // // Author : ?? // // email : @ // // Purpose : ?? // // Distribution : // // Use : // ?? // // Todo : // O ?? // // History : // 19??/??/?? : Mr ?Name? : ?What? // // /////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////// // // #include "XXX.hh" #if defined(NO_DEBUG) #define ASSERT(x) #else //defined(NO_DEBUG) #define ASSERT(x) if(!(x)) \ { cerr << "Assertion failed : (" << #x << ')' << endl \ << "In file : " << __FILE__ << "at line #" << __LINE__ << endl \ << "Compiled the " << __DATE__ << " at " << __TIME__ << endl; abort();} #endif // else defined(NO_DEBUG) const char* const XXX_RCS_ID = "@(#)class XXX definition."; /////////////////////////////////////////////////////////////// // class XXX /////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////// // Standard services - public : XXX::~XXX(void) { } /////////////////////////////////////////////////////////////// // Interface - public : void XXX::SelfDisplay(ostream& thatStream) const { thatStream << "< class XXX >" << endl; }

Timothe Royer (royer@uranus.crosat.fr)

version : 1997-02-25

page 69

Guide de dveloppement logiciel en C++


bool XXX::OK(void) const { return true; }

C++ coding guide

/////////////////////////////////////////////////////////////// // Internals - private : // // ///////////////////////////////////////////////////////////////

Timothe Royer (royer@uranus.crosat.fr)

version : 1997-02-25

page 70

Guide de dveloppement logiciel en C++

C++ coding guide

Annexe D - Exemple de classe : un noeud de liste doublement chane


FIXME add template FIXME ordre mth FIXME pas de for, if, else, switch. FIXME si pas de templates T* -> void*

Timothe Royer (royer@uranus.crosat.fr)

version : 1997-02-25

page 71

Guide de dveloppement logiciel en C++

C++ coding guide

/////////////////////////////////////////////////////////////// // // File name : DLLNode.hh // // Creation date : 1995/06/?? // // Version : 1995/09/29 // // Author : Timothe'e Royer // // Email : tim@puff.frmug.fr.net // // Purpose : // // Distribution : Free without warranty. // // Todo : // X templates // O ajouter un tri. // O ajouter un scanner. // // History : // 1995/09/28 : suppr. SetNext()/SetPrev() // 1995/09/29 : templates // /////////////////////////////////////////////////////////////// #if !defined(DLLNode_hh) #define DLLNode_hh static const char* const DLLNodeId = "@(#)class DLLNode declaration and definition"; /////////////////////////////////////////////////////////////// // template <class T> class DLLNode { public : // Standard inline DLLNode(void); inline ~DLLNode(void); // Services inline inline inline inline T& GetNext(void) const; T& GetPrev(void) const; void Insert(T& ThatNewNode); void Remove(void);

private : // Datas T* Prev; T* Next; // Hidden inline DLLNode(DLLNode&); inline operator =(DLLNode&);

Timothe Royer (royer@uranus.crosat.fr)

version : 1997-02-25

page 72

Guide de dveloppement logiciel en C++

C++ coding guide

}; /////////////////////////////////////////////////////////////// // Standard /////////////////////////////////////////////////////////////// template <class T> DLLNode<T>::DLLNode(void) Next((T*)this) { } template <class T> DLLNode<T>::~DLLNode(void) { Remove(); } // Services /////////////////////////////////////////////////////////////// template <class T> T& DLLNode<T>::GetNext(void) const { return *Next; } template <class T> T& DLLNode<T>::GetPrev(void) const { return *Prev; } template <class T> void DLLNode<T>::Insert(T& ThatNewNode) { ThatNewNode.Prev = (T*)this; ThatNewNode.Next = Next; Next->Prev = &ThatNewNode; Next = &ThatNewNode; } template <class T> void DLLNode<T>::Remove(void) { Prev->Next = Next; Next->Prev = Prev; Next = (T*)this; Prev = (T*)this; } // /////////////////////////////////////////////////////////////// #endif // !defined(DLLNode_hh) : Prev((T*)this),

Timothe Royer (royer@uranus.crosat.fr)

version : 1997-02-25

page 73

Guide de dveloppement logiciel en C++

C++ coding guide

/////////////////////////////////////////////////////////////// // // // File name : DLLNodeTester.cc // // Creation : 19??/??/?? // // Version : 19??/??/?? // // Author : ?? // // email : @ // // Purpose : Test class DLLNode. Illu strate its use & interface. // // Distribution : // // Use : // ?? // // Todo : // O ?? // // History : // 19??/??/?? : Mr ?Name? : ?What? // // /////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////// // // #include "DLLNodeTester.hh" #if defined(NO_DEBUG) #define ASSERT(x) #else //defined(NO_DEBUG) #define ASSERT(x) if(!(x)) \ { cerr << "Assertion failed : (" << #x << ')' << endl \ << "In file : " << __FILE__ << "at line #" << __LINE__ << endl \ << "Compiled the " << __DATE__ << " at " << __TIME__ << endl; abort();} #endif // else defined(NO_DEBUG) const char* const DLLNodeTester_RCS_ID = "@(#)class DLLNode tester."; /////////////////////////////////////////////////////////////// // class DLLNode tester /////////////////////////////////////////////////////////////// #include <string.h> class Man : public DLLNode<Man> { public : Man(const char* const creationName); ~Man(void); const char* Name(void) const; private : // Data char* DataName; // Hidden

Timothe Royer (royer@uranus.crosat.fr)

version : 1997-02-25

page 74

Guide de dveloppement logiciel en C++


inline Man(const Man&); inline void operator =(const Man&); }; Man::Man(const char* const creationName) { strdup(DataName,creationName); } Man::~Man(void) { delete DataName; } const char* Man::Name(void) const { return DataName; }

C++ coding guide

/////////////////////////////////////////////////////////////// // // int main(const int, const char** const, const char** const) { Man titi("titi"); Man toto("toto"); // FIXME stress test return 0; } // // ///////////////////////////////////////////////////////////////

Timothe Royer (royer@uranus.crosat.fr)

version : 1997-02-25

page 75

Guide de dveloppement logiciel en C++

C++ coding guide

Annexe E - Exemple de scurisation : La classe Cookie


FIXME explications FIXME Tester

Timothe Royer (royer@uranus.crosat.fr)

version : 1997-02-25

page 76

Guide de dveloppement logiciel en C++

C++ coding guide

/////////////////////////////////////////////////////////////// // // Filename : Cookie.hh // // Directory : $(HOME)/Classes/Cookie // // Creation : 1995/05/19 // // Version : 1995/05/19 // // Author : Timothee Royer // // Purpose : Base class -> inheritance. // // Use : Simply inherit from this class. Use Cookie : :OK() to know if you are // allocated. // /////////////////////////////////////////////////////////////// #if !defined(Cookie_hh) #define Cookie_hh #include <iostream.h> // I use #define here cause I got no .cc file #define COOKIE_ALLOCATED 0xBADF0E #define COOKIE_CONSTRUCTED 0xF001BABE #define COOKIE_DELETED 0xDEADBEEF #if defined(NO_DEBUG) #define ASSERT(x) #else //defined(NO_DEBUG) #define COOKIE_ASSERT(x) if(!(x)) \ { cerr << "Cookie assertion failed : (" << #x << ')' << endl \ << "In file : " << __FILE__ << "at line #" << __LINE__ << endl \ << "Compiled the " << __DATE__ << " at " << __TIME__ << endl; abort();} #endif // else defined(NO_DEBUG) class Cookie { public : // Standards Cookie(void) { Data = COOKIE_CONSTRUCTED; } ~Cookie(void) { COOKIE_ASSERT(Data != COOKIE_CONSTRUCTED); Data = COO KIE_DELETED; } // Interface void Construct(void) {

Timothe Royer (royer@uranus.crosat.fr)

version : 1997-02-25

page 77

Guide de dveloppement logiciel en C++


Data = COOKIE_CONSTRUCTED; } bool OK(void) const { ASSERT(COOKIE_ALLOCATED) return 1; } private : // Data int Data; // Hidden Cookie(const Cookie&); operator =(const Cookie&); }; #undef COOKIE_ALLOCATED #undef COOKIE_CONSTRUCTED #undef COOKIE_DELETED #endif // !defined(Cookie_hh)

C++ coding guide

Timothe Royer (royer@uranus.crosat.fr)

version : 1997-02-25

page 78

Guide de dveloppement logiciel en C++

C++ coding guide

Annexe F - Lexique
Macro Une macro est lquivalent dune fonction, mais elle est traite par le prprocesseur. Elle est dfinie avec la directive #define . Exemple classique de dfinition de macro : #define MAX(x,y) {(x)>(y) ?(x) :(y)} lutilisation le code : toto = MAX(titi,10) ; Sera en fait remplac par celui-ci, avant la compilation : toto = {(titi)>(10) ?(titi) :(10)} ; Lusage des macros nest pas recommand. Compltion FIXME Instance Assert Transtypage/coercition : cast extern inline Une fonction ou une mthode peut tre dclare inline. Dans ce cas, lors de la construction du code excutable, le corps de cette fonction est insr chaque appel. Le code gnr est plutt plus gros et plus rapide. mutable Donne membre modifiable mme lorsquelle appartient une instance constante. profiler Un profiler est outil qui permet de dterminer comment est consomm le temps machine lors de lexcution dun programme. Son usage est indispensable pour toute optimisation. transtyper surcharger dfinition Dclaration Dpendant de limplmentation : invariant assertion polymorphisme lvalue rvalue

Timothe Royer (royer@uranus.crosat.fr)

version : 1997-02-25

page 79

Guide de dveloppement logiciel en C++

C++ coding guide

Annexe G - Historique
1995/08/23 Semblant de mise en chapitres 1995/08/31 Accents ! (merci Mop) 1995/09/06 Justification ! 1995/09/08 wc (nb. de lignes, mots et caractres) : 1586 7551 51981 1995/09/11 (0)(1)(2) -> *** IMPRATIF *** [...] [...] Dbut de lexique. 1995/09/12 Dbut de plan ! wc : 1850 8424 60053 1995/09/15 Dcimation des FIXME wc : 2015 9560 67241 Nb 2 rgles : imp :50 | rec :62 | am :37 + (Pq ?) :72 | (Ex) :187 ? | Fix :87 1995/09/19 Nouvelle prsentation des rgle : nivo ness / 1 l. part. wc : 2339 11002 76095 Premire beta version dlivre. 1995/10/01 #FIXME : 44 -> 14 wc : 2898 13887 97344 Deuxime beta version dlivre 1996/07/04 Version Word Ennopncs griss. P: 96 - l:2668 - parag:1916 - mots:16644 - cars:92053 1996/08/14 Corrections != / add Mix c & c++ / nouvox FIXME 81 pages, 18770 mots, 104608 car, 2273 parag, 3135 lignes 1997/02/21 Le guide intresse quelques centres de dveloppement de larme de terre franaise. Le guide est propos freeware sur internet (www.mygale.org/00/jebdoun). Le guide est rfrenc dans plusieurs (+- 15) moteurs de recherche mondiaux et francophones. (Hier) Annonce de la prsence du guide sur fr.comp.soft.objet. (Hier).

Timothe Royer (royer@uranus.crosat.fr)

version : 1997-02-25

page 80

Guide de dveloppement logiciel en C++


Le mot norme napparat plus dans le document. Rcriture du rsum et dune introduction. Nouveaux entte et pied de page.

C++ coding guide

Timothe Royer (royer@uranus.crosat.fr)

version : 1997-02-25

page 81

Guide de dveloppement logiciel en C++

C++ coding guide

ATTENTION. MERCI DE NE PAS FAIRE CIRCULER CE DOCUMENT QUI EST UNE BETA VERSION. LE TEXTE FINAL SERA MIS EN LIBRE DISTRIBUTION, APRES UNE PERIODE DE TESTS SUFFISANTE. SI VOUS ETES INTERESSES PAR CE DOCUMENT ET EN PARTICULIER SI VOUS SOUHAITEZ ETRE BETA-TESTEUR, CONTACTEZ-MOI DIRECTEMENT A tim@puff.frmug.fr.net. AVERTISSEMENT. CE GUIDE EST FOURNI TEL QUEL, SANS AUCUNE GARANTIE EXPLICITE OU IMPLICITE. EN PARTICULIER, LAUTEUR DCLINE TOUTE RESPONSABILIT QUANT AUX CONSQUENCES DE LUSAGE DE CE GUIDE. MERCI DE MAIDER RALISER CE DOCUMENT EN ME FAISANT PARVENIR VOS CORRECTIONS, VOS IMPRESSIONS, LES BESOINS DE PRCISIONS ET SURTOUT VOS PROPOSITIONS DAMLIORATIONS. EN PARTICULIER, LES VOLONTAIRES POUR MAIDER COCHER LES LIGNES DU TODO CI-DESSOUS SONT LES BIENVENUS. CE GUIDE EST EN DVELOPPEMENT ET VOLUE RAPIDEMENT, JE VOUS INVITE DONC ME RETOURNER VOS IDES RAPIDEMENT, POUR VITER DES DOUBLES CORRECTIONS OU UN TRAVAIL INUTILE SUR UN TEXTE DPASS. Copyright 1995 Timothe Royer Author : Timothe Royer Cration : 1995/07/21 (< ?) Version : 1995/09/29 Todo : ( Lgende o : faire | / : commenc | x : fait ) o Script -> instancier fichier exemple o Filtre yacc/lex. / Redfinir la table des matires. x Diffrencier C/C++. / Describe Implementation dependant features / multi-plateform dvpt o numrotation des rgles x Marques de rvision x Warn op = != const/copy o Reprendre les exemples et les explications / Bibliographie : relire, indiquer refs et crire descr. x Amliorer la prsentation de ce texte : lignes vides, titres... o include C++ ISO std working paper recomm , Beta testeurs o cf. gnu licence / Lexique o Traduction en anglais o Index o Amliorer la prsentation o Complter le Todo o Pour chaque rgle : req. lvl, #, texte, (Pourquoi ?), (Rappel), (Comment ?), (Exception), (Exemple). o Annexer 1 formulR de proposition de modificat de la norme o Implmenter un lint -> la norme / crire un XXXTester.cc o Version courte / longue. o Fournir des classes compatibles STL. Timothe Royer (royer@uranus.crosat.fr) version : 1997-02-25 page 82

Guide de dveloppement logiciel en C++

C++ coding guide

o Phrases + courtes o Lister les textes de rgles part. o Chapitre conclusion ? x document matre + esclaves sous Word. string.h != strings.h taille fich < 10000 lignes num les rgles ? utiliser long et non int dans les exemples. o Extern "C" o Enlever les "on" et les "nous". Traduire les citations ID : utiliser des gris != pour imperatif, recomm, amelior ? (cadre, police ?) ordre d'inclusion des .h => PB grave profiling liste de Cast srs ./ types de base expliquer #include "" != <> Hritage multiple pas de }; Les params sont : soit &const (const pour type de base), soit & (pour tre modifis). (& pb appel -> cstr / copie) Nouvelle intro : Rgles emmergentes collectives / Choses faire pour viter les problmes. Proposer template <T> MAX(const T&, const T&).... (!= #define MAX(x,y)) Utiliser const & ref -> donnes mb. Utiliser des Accesseur getZ() et setZ() (!= T& setZ(void)) Hritages multiples if(t==true) != if(t) Figures ? ? ? Marques de rvision : [#####] (*****) {%%%%%} <OOOOO>

Timothe Royer (royer@uranus.crosat.fr)

version : 1997-02-25

page 83