Lorsquun programme devient volumineux, il est peu rentable de le placer dans un seul fichier : la compilation devient très longue, puisquil faut tout recompiler chaque fois, et il est difficile de sy retrouver.
Pour simplifier alors le travail, on répartit les différentes routines dans plusieurs fichiers source (*.cpp
). Chacun de ces fichiers peut alors être compilé indépendamment, produisant un fichier objet (*.obj
). Lensemble des fichiers objets est ensuite regroupé par léditeur de liens pour former un programme exécutable unique (*.exe
, sur PC).
Dans la pratique, les différents fichiers doivent dabord répondre à une certaine logique. Par exemple, on place fréquemment dans un fichier séparé une classe entièrement implémentée, ou deux ou trois si elles sont reliées. Il ne faut surtout pas répartir au hasard les fonctions dans plusieurs fichiers, car il serait vite très difficile de sy retrouver.
Comme les fichiers doivent communiquer entre eux, il faut au moins un fichier en-tête qui leur soit commun. En pratique, on utilise même souvent un fichier en-tête pour chaque source, sauf celui qui contient main
; en général on donne à ce fichier le même nom avec le suffixe *.h
(mais ce nest nullement obligatoire). Lorsquon utilise cette méthode très fréquente, le fichier en-tête peut être considéré comme linterface dun module dont le fichier source est limplantation. Dans ce fichier den-tête on placera toutes les déclarations (de classes, de fonctions, de constantes, de variables, etc.) susceptibles dêtre utilisées par les autres. On noubliera pas les fonctions en ligne, car le compilateur doit les connaître entièrement pour les placer directement dans le code produit.
Dans le fichier source proprement dit, on trouvera généralement une directive dinclusion du fichier en-tête correspondant, suivie éventuellement dautres directives dinclusion, soit pour les librairies standard, soit pour les autres fichiers en-têtes du même programme utilisés par le fichier courant. On trouvera ensuite limplantation des fonctions qui ne sont pas en ligne.
Dans le fichier contenant main
, on trouvera toutes les inclusions den-têtes nécessaires, suivies par main
et éventuellement quelques fonctions étroitement liées à elle.
Prenons un exemple simple (et artificiel). Imaginons un programme faisant des calculs sur des matrices de fractions, implantées par une classe spéciale du même genre que la classe liste
, mais adaptée aux fractions. Un tel programme pourrait être réparti dans cinq modules différents :
fraction.cpp
: Fichier contenant la classe fraction
et les opérations sur elle. listefra.cpp
: Fichier contenant les classes noeud
et liste
nécessaires à la gestion de listes chaînées de fractions. matrfra.cpp
: Fichier contenant la classe matrice
utilisant les listes de fractions, et les opérations sur elle. iosfract.cpp
: Fichier contenant diverses fonctions décriture et de lecture de fractions et de matrices. mainfra.cpp
: Fichier contenant main
et la gestion de base du programme (commandes, etc.). En réalité, les quatre premiers modules sont répartis chacun en un en-tête et un source. Voici lallure quils pourraient avoir :
#ifndef _FRACTION_H#define _FRACTION_Hclass fraction { // ....définition de la classe // avec fonctions en ligne éventuelles }; #endif
#include "fraction.h" // ici implantation des fonctions membres de // la classe fraction qui ne sont pas en ligne.
#ifndef _LISTEFRA_H#define _LISTEFRA_H #include "fraction.h"class noeud_fra; // définition dans listefra.cpp class liste_fra { // listes de fractions }; #endif
#include "fraction.h" class noeud_fra { // ...classe utilisée seulement par liste_fra };// implantations des fonctions de noeud_fra et// liste_fra qui ne sont pas en ligne
#ifndef _MATRFRA_H#define _MATRFRA_H #include "fraction.h"class matrice_fra { // ... }; #endif
#include "listefra.h"#include "matrfra.h" // implantations des fonctions membres de matrice_fra// qui ne sont pas en ligne
#ifndef _IOSFRACT_H#define _IOSFRACT_H #include "matrfra.h"// déclarations de routines daffichage et de lecture// de fractions et de matrices #endif
#include <iostream.h>#include "iosfract.h" // implantations des routines définies dans iosfract.h
#include "iosfract.h" main(){ // ...}
Voilà notre programme bien silhouetté. On notera que chaque fichier ninclut que les en-têtes dont il a besoin. Par exemple, les listes de fractions ne sont utilisées que par limplantation des matrices ; elles napparaissent donc que dans matrfra.cpp
, et non dans matrfra.h
ni dans les autres. De même, les flots dentrées-sorties déclarés dans <iostream.h>
ne sont employés que par limplantation des fonctions dentrées-sorties des fractions et matrices.
On remarquera aussi une écriture classique dans les fichiers en-têtes, avec la clause #ifndef _XXX_H
suivie par un #define _XXX_H
. Ceci permet de ninclure quune seule fois un même fichier den-tête dans un fichier. Cela peut paraître inutile, mais remarquez que listefra.h
et matrfra.h
incluent tous deux fraction.h
, et sont tous deux inclus dans matrfra.cpp
. Dès lors, si lon oublie dutiliser cette clause conditionnelle, c
sera inclus deux fois dans c
, ce qui non seulement augmentera le temps de compilation mais aussi risque de provoquer une erreur car le compilateur nacceptera pas que lon définisse deux fois certains éléments du fichier (variables globales, etc.).
On peut à présent compiler chacun de ces fichiers, soit séparément, soit ensemble (avec un projet, voir ci-après). On obtient alors sur disque cinq fichiers objets fraction.obj
, ...
, mainfra.obj
. Reste à les relier entre eux, ainsi quà la librairie standard, pour obtenir le programme souhaité. Cela est fait par lintermédiaire dun projet, que nous allons examiner à présent.
Précédent | Sommaire | Suivant |