Date de première publication : 2022/01/12
L'objectif de cette page est de revenir sur la notion de variable globale avec la compilation séparée. Cela a été vu en ZZ1 mais un petit rappel ne fait pas de mal !
Un seul fichier
Si on met tout dans un seul fichier, cela donne :
#include <iostream>
class Bavarde {
public:
int get() { return 1; }
}
Bavarde globale;
int main(int, char **) {
std::cout << globale.get() << std::endl;
return 0;
}
globale
est une variable globale, initialisée avec le constructeur par défaut fourni par le compilateur et le code marche nickel! (enfin presque :-))
$ g++ main.cpp
$ a.out
Si on met maintenant la classe Bavarde
soit complètement dans un fichier d'entête (ou un fichier d'entête et un fichier d'implémentation), cela ne change rien !
// Bavarde.hpp
#include <iostream>
class Bavarde {
public:
int get() { return 1; }
};
Bavarde globale;
// main.cpp
#include "Bavarde.hpp"
int main(int, char **) {
std::cout << globale.get() << std::endl;
return 0;
}
Une variable commune à plusieurs fichiers
On va maintenant voir ce qu'il se passe quand la variable globale est utilisée dans plusieurs fichiers...
Ajoutons une fonction utilisation()
déclarée dans un fichier d'entête et un fichier d'implémentation
// utilisation.hpp
int utilisation();
// utilisation.cpp
#include <iostream>
#include "utilisation.cpp"
void utilisation() {
std::cout << globale.get() << std::endl;
}
On peut compiler sans problème les fichiers objets :
$ g++ main.cpp -o
$ g++ utilisation.cpp -o
La problème arrive à l'édition des liens :
$ g++ main.o utilisation.o
On obtient un message DUPLICATE SYMBOL
// bavarde.hpp
#ifndef __BAVARDE_HPP
#define __BAVARDE_HPP
// le code initial
#endif
Et oui, les gardiens, cela marche et il faut les mettre car c'est indispensable pour éviter les inclusions multiples de fichiers mais dans le cas présent, notre intuition n'est pas bonne : la compilation lit bien les fichiers hpp pour chaque translation unit donc le fichier est lu une première fois pour main.cpp
puis une deuxième fois pour utilisation.cpp
. Quand on met les fichiers ensemble, ça coince !
Solution
Si on veut s'arranger pour n'avoir qu'un seul exemplaire de la variable globale bavarde, il va falloir la déclarer comme externe ...
// bavarde.hpp
#ifndef __BAVARDE_HPP
#define __BAVARDE_HPP
#include <iostream>
class Bavarde {
public:
int get() { return 1; }
};
extern Bavarde globale;
#endif
Si vous faites cela, la transformation en fichiers objet se déroule sans problème mais là encore, cela coince à l'édition des liens.
On obtient un message MISSING SYMBOL
Il faut désormais définir la variable globale dans un fichier de code : main.cpp
, bavarde.cpp
ou utilisation.cpp
// main.cpp
// derniere version : on choisit main.cpp
#include "Bavarde.hpp"
Bavarde globale;
int main(int, char **) {
std::cout << globale.get() << std::endl;
return 0;
}
J'espère que tout cela à éclairci le schmilblick. Bonnes compilations futures !!!