Précédent Index Suivant

Chapitre 5   Environnement de développement C++

5.1   Compilation

5.1.1   Compilation d'une classe C++

Le compilateur traduit le code C++ en code objet (ou code ``machine''). Pendant ce cours, nous utiliserons le compilateur Gnu C++, appelé g++. Ce compilateur a beaucoup d'options, et nous vous incitons à vous reporter aux pages de manuel pour plus d'information. Toutefois, voici les options les plus utiles :
-c
--- Compiler les fichiers sources, mais sans édition de liens. Le compilateur produit un fichier objet .o pour chaque fichier source.
-o fichRes
--- Donner le nom fichRes au fichier exécutable produit par la compilation et l'édition de liens. Quand cette option n'est pas employée, le nom d'exécutable par défaut est a.out.
-g
--- engendrer de l'information pour le débogage, à utiliser avec un débogueur comme gdb (ou son interface utilisateur ddd).
Pour compiler une classe C++ Toto (que nous supposerons correcte), la commande à lancer est :
g++ -c Toto.cpp
Si la compilation s'achève sans erreur, vous récupérez dans votre répertoire un fichier objet appelé Toto.o.

5.1.2   Compilation d'un programme C++

Un programme C++ est un fichier qui
  1. inclut les headers de toutes les classes et bibliothèques employées,
  2. comporte une fonction main : int main() ...corps du programme....
Pour compiler un programme C++, appelons-le monprog.cpp, voici la marche à suivre :
g++ monprog.cpp C_1.o C_2.o ... C_n.o -o monprog
C_1.o C_2.o ... C_n.o sont les fichiers objets des classes compilées au préalable, et utilisées par le programme monprog.cpp. L'option -o indique que le fichier produit par la compilation doit s'appeler monprog. C'est un exécutable pour l'architecture et le système sur lesquels vous avez compilé1 le programme. Le compilateur a besoin du nom des fichiers objets pour les passer à l'édition de liens, qui ``assemble'' les différents morceaux compilés pour en faire un fichier exécutable unique.

5.1.3   Construire des bibliothèques

Si vous créez un certain nombre de classes, vous ressentirez rapidement le besoin de réunir les versions compilées de ces classes dans une bibliothèque. En Unix ou Linux, pour réunir des fichiers objets, on utilisera typiquement la commande ar, qui permet de créer des fichiers ``archives'' :
ar r mabib.a C_1.o C_2.o ... C_n.o
L'option r indique qu'il faut remplacer les versions existantes éventuelles des fichiers C_i.o par les nouvelles versions fournies ; s'il n'y a pas de version existante, le fichier est ajouté à l'archive.

Pour faire d'un fichier archive une bibliothèque utilisable, il faut construire un index de toutes les fonctions et variables définies par les différents fichiers objets qu'elle contient. Ceci est fait par la commande ranlib, qu'on appelle souvent systématiquement après ar :
ranlib mabib.a
La figure suivante résume les différentes étapes que nous avons décrites :
monprog.cpp @>g++>> monprog @<Edition de liens<< mabib.a @<ar<ranlib<
{
C_1.o @<g++<< C_1.cppC_2.o @<g++<< C_2.cpp...C_n.o @<g++<< C_n.cpp

5.2   Le préprocesseur

Les fichiers headers sont ajoutés au programme à compiler par les instructions d'inclusion du préprocesseur. Le préprocesseur traite le fichier source avant la compilation proprement dite. Il effectue surtout des remplacements lexicographiques sur le texte à compiler.

Les instructions du préprocesseur commencent toutes par le caractère # ; nous présentons ci-après les plus utiles d'entre elles.

5.2.1   L'instruction #include

L'instruction #include a la syntaxe suivante :

#include <some_lib>
#include "my_file.hh"
La première ligne indique qu'on inclut some_lib, qui est le fichier header d'une bibliothèque, et qu'on trouvera dans un chemin appartenant à une liste connue2. La seconde ligne indique l'inclusion d'un fichier header fourni par l'utilisateur, avec un chemin relatif à l'emplacement du fichier source qui contient l'instruction d'inclusion.

Prenons comme exemple que nous voulons construire une classe ManuscritAncien, définie par un fichier ManuscritAncien.hh contenant l'interface de la classe, et un fichier ManuscritAncien.cpp contenant la mise en oeuvre de la classe. Le fichier ManuscritAncien.hh pourrait être de la forme :
#include "Ouvrage.hh"
#include "Livre.hh"

class ManuscritAncien : public Livre  {
    ...
    corps de ManuscritAncien.hh
    ...
};
et le fichier ManuscritAncien.cpp :
#include <iostream>
#include "ManuscritAncien.hh"
....
body of ManuscritAncien.cpp
....
S'il manque l'inclusion (éventuellement en cascade) des interfaces de toutes les bibliothèques et classes utilisées, la compilation échouera...

NB : il faut minimiser les dépendances entre modules en n'incluant que les directives #include strictement nécessaires à la compilation d'un module donné.

5.2.2   Inclusion conditionnelle

L'instruction #if(n)def-define-endif sert (entre autres) à éviter les inclusions multiples du même fichier :

#ifndef __POLYCOPIE_HH_INCLUDED__
#define __POLYCOPIE_HH_INCLUDED__
// définition de Polycopie.hh
#endif /* __POLYCOPIE_HH_INCLUDED__ */
teste si __POLYCOPIE_HH_INCLUDED__ a déjà été défini C'est une constante du préprocesseur (à écrire en majuscules). Si __POLYCOPIE_HH_INCLUDED__ n'a pas encore été défini, // définition de Polycopie.hh est inclus et traité, sinon il n'est pas inclus.

Nous ajoutons #define __POLYCOPIE_HH_INCLUDED__ juste après l'instruction #ifndef pour garantir que la constante __POLYCOPIE_HH_INCLUDED__ sera définie la première fois que le fichier header est mentionné dans une directive d'inclusion.

La directive #ifdef est souvent utilisée pour faire de la compilation conditionnelle de morceaux de code, par exemple à des fins de débogage :

int main () {
#ifdef DEBUG
  cout << "begin of main()"<< endl;
#endif
 ....
 // body of main()
 ....
#ifdef DEBUG
  cout << "end of main()"<< endl;
#endif
}

5.3   Où trouver un compilateur C++ ?

Il existe un vaste choix de compilateurs C++ commerciaux. Des solutions libres et gratuites existent également, quelques pointeurs sont donnés ici :
1
Le compilateur C++ produisant directement du code machine, et non du bytecode comme en Java, un programme compilé sous un système et sur une architecture n'a aucune raison de s'exécuter sur d'autres architectures et/ou systèmes !
2
Grâce à l'option -I du compilateur, vous pouvez ajouter vos propres chemins aux chemins prédéfinis.
3
http://sourceforge.net/projects/mingw/.

Précédent Index Suivant