tete du loic

 Loïc YON [KIUX]

  • Enseignant-chercheur
  • Référent Formation Continue
  • Responsable des contrats pros ingénieur
  • Référent entrepreneuriat
  • Responsable de la filière F2 ingénieur
  • Secouriste Sauveteur du Travail
mail
loic.yon@isima.fr
phone
(+33 / 0) 4 73 40 50 42
location_on
ISIMA
  • twitter
  • linkedin
  • viadeo

[C++] Variable globale

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 !!!