2. Compilation : plus de détails
Objectifs
- Apprendre à séparer les définitions des déclarations des fonctions
- Prémisse de structure du projet (workflow)
Séparer la déclaration et la définition
En C++
, il est commun de séparer “physiquement” la déclaration des fonctions de leur définition. La déclaration fournit au compilateur les informations sur les arguments entrants et sortants de la fonction (quel type ?), tandis que la définition détaille le fonctionnement interne de la fonction (comment ça marche ?). En pratique, les déclarations sont stockées dans un fichier .h
(ou .hpp
), appelé fichier d’en-têtes ou fichier header (ou header), tandis que les définitions le sont dans les fichiers .cpp
, appelés fichiers sources.
Reprenons l’exemple du Hello World
et réécrivons le dans la structure proposée. Au lieu de disposer d’un seul fichier, nous en avons maintenant trois :
hello.cpp
contiendra les définitions des fonctions dont nous avons besoinhello.hpp
contiendra les déclarations des fonctions dont nous avons besoin (les header)main.cpp
qui contiendra le programme à exécuter
Et nous avons alors, dans l’ordre
// fichier hello.hpp
#pragma once // voire remarque ci-dessous
void hello_world(); // Déclaration de la fonction
// fichier hello.cpp
#include "hello.hpp"
#include <iostream> //cin et cout
// Déclaration de la fonction
void hello_world(){
std::cout << "Hello World!";
}
// fichier main.cpp
#include "hello.hpp"
int main(){
hello_world(); //appel de la fonction
}
Afin d’éviter que le fichier d’en-tête soit inclus plusieurs fois à travers le projet, les fichiers d’en-tête .hpp
commenceront systématiquement par # pragma once
. Remarquez que cette commande nécessite le C++ 11
(ce que nous encourageons !):
g++ -std=c++11
Nous pouvons utiliser des chevrons ou des guillemets pour les #include
:
#include <tableau.hpp> // chevrons
#include "tableau.hpp" // guillemets
La différence réside dans la recherche du fichier effectuée par le compilateur :
- Chevrons : la recherche s’effectue dans les dossiers désignés par le pré-processeur. C’est donc cela que nous utilisons pour les bibliothèques standards.
- Guillemets : le compilateur recherche d’abord dans le répertoire courant (celui du fichier appelant) puis, en cas d’échec, suit la règle de recherche des chevrons. Les guillemets sont (en général) à privilégier pour vos fichiers d’en-tête.
Compilation
Compilons maintenant notre petit projet avec la commande suivante :
g++ main.cpp hello.cpp -o main -std=c++11
Ou alors nous pouvons générer les fichiers objets des différents fichiers source puis créer l’exécutable à partir des sources :
# Compilation de hello
g++ -c hello.cpp -o hello.o -std=c++11
# Compilation du main
g++ -c main.cpp -o main.o -std=c++11
# Édition des liens
g++ main.o hello.o -o main -std=c++11
# Lancement de l'exécutable
./main
En vous inspirant fortement de ce qui précère, vous devez construire une fonction qui rempli un tableau de type std::vector<int>
avec des nombres de 1 à n :
- Créez deux fichiers
tableau.cpp
ettableau.hpp
- Dans le fichier d’en-tête (header), déclarez une fonction de prototype :
void remplissage(std::vector<int> &tableau, int n);
Vous aurez besoin d’ajouter en haut du fichier :
#include <vector>
- Définissez dans le fichier source associé. La fonction doit redéfinir la taille du tableau
tableau
àn
et le remplir de nombres entiers1 de 1 à n. - Appelez cette fonction dans le fichier
main.cpp
. - Compilez et exécutez le programme.
- Vérifiez que ça fonctionne en affichant chaque valeur du tableau.
-
utilisez de préférence les méthodes
clear()
etpush_back()
↩︎