553 votes

Quand utiliser extern en C++

Je lis "Pensez en C++" et ça vient juste d'introduire la déclaration extern. Par exemple :

extern int x;
extern float y;

Je pense comprendre la signification (déclaration sans définition), mais je me demande dans quel cas cela s'avère utile.

Est-ce que quelqu'un peut fournir un exemple ?

1 votes

J'ai dû fournir une définition avec extern à plusieurs reprises. Les outils Microsoft ont produit une erreur de lien pour des symboles manquants lorsque les tables dans un autre fichier source étaient seulement définies. Le problème était que la table était const et que le compilateur C++ l'a promue en static dans l'unité de traduction. Voir, par exemple, ariatab.cpp et kalynatab.cpp.

4 votes

Et je pense que la réponse de Nik est la bonne car il semble être le seul à avoir répondu à une question sur C++. Tous les autres semblent avoir digressé vers une question sur C.

4 votes

À partir de C++17, inline peut être une meilleure façon d'accomplir ce que vous voulez faire avec extern. Avec inline, vous pouvez définir des variables globales dans les en-têtes et ne pas vous soucier des problèmes de définitions multiples. stackoverflow.com/questions/38043442/…

733voto

dreamlax Points 47152

Cela s'avère utile lorsque vous avez des variables globales. Vous déclarez l'existence de variables globales dans un en-tête, de sorte que chaque fichier source incluant l'en-tête en soit informé, mais vous n'avez besoin de le “définir” qu'une seule fois dans l'un de vos fichiers source.

Pour clarifier, en utilisant extern int x;, vous indiquez au compilateur qu'un objet de type int appelé x existe quelque part. Ce n'est pas au compilateur de connaître l'endroit où il se trouve, il a simplement besoin de connaître le type et le nom pour savoir comment l'utiliser. Une fois que tous les fichiers source ont été compilés, le linkeur résoudra toutes les références de x vers la seule définition qu'il trouve dans l'un des fichiers source compilés. Pour que cela fonctionne, la définition de la variable x doit avoir ce qu'on appelle une “liaison externe”, ce qui signifie essentiellement qu'elle doit être déclarée en dehors d'une fonction (ce qu'on appelle généralement “la portée du fichier”) et sans le mot-clé static.

en-tête :

#ifndef HEADER_H
#define HEADER_H

// tout fichier source incluant ceci pourra utiliser "global_x"
extern int global_x;

void print_global_x();

#endif

source 1 :

#include "header.h"

// comme global_x doit encore être défini quelque part,
// nous le définissons (par exemple) dans ce fichier source
int global_x;

int main()
{
    // définir global_x ici :
    global_x = 5;

    print_global_x();
}

source 2 :

#include 
#include "header.h"

void print_global_x()
{
    // afficher global_x ici :
    std::cout << global_x << std::endl;
}

27 votes

Merci. Donc, si je déclare une variable globale dans un fichier d'en-tête sans le mot-clé extern, les fichiers source qui incluent l'en-tête ne la voient pas?

38 votes

Vous ne devriez pas déclarer de variables globales dans un en-tête, car lorsque 2 fichiers incluent le même fichier d'en-tête, ils ne seront pas liés (le lien émettra une erreur concernant un "symbole en double")

94 votes

@Aslan986: Non, quelque chose de pire se produit. Chaque fichier source qui inclut l'en-tête aura sa propre variable, donc chaque fichier source se compilera indépendamment mais le lien donnera une erreur parce que deux fichiers source auront les mêmes identifiants globaux.

255voto

MByD Points 78505

Il est utile lorsque vous partagez une variable entre quelques modules. Vous la définissez dans un module, et utilisez extern dans les autres.

Par exemple :

dans le fichier1.cpp :

int global_int = 1;

dans le fichier2.cpp :

extern int global_int;
//dans une fonction
cout << "global_int = " << global_int;

63 votes

Cette réponse est plus correcte que celle acceptée, car elle n'utilise pas de fichier d'en-tête et indique clairement qu'il est utile uniquement lors du partage entre quelques modules. Pour les applications plus grandes, il est préférable d'utiliser par exemple une classe ConfigManager.

2 votes

Y a-t-il des pièges lorsque des espaces de noms sont impliqués, global_int est dans l'espace de noms global, si je devais l'utiliser dans le fichier2.cpp dans une section d'espace de noms, je devrais le mettre correctement en portée ? c'est-à-dire namespace XYZ{ void foo(){ ::global_int++ } };

13 votes

@Zac : En revanche, en ne déclarant pas de variable globale dans un en-tête, tu as involontairement rendu beaucoup plus difficile de déterminer où elle est réellement définie. Habituellement, si tu vois une variable globale déclarée dans abc.h, il y a de fortes chances qu'elle soit définie dans abc.cpp. Un bon IDE aidera toujours, mais un code bien organisé est toujours une meilleure solution.

97voto

Il Divin Codino Points 1708

C'est tout à propos de la liaison.

Les réponses précédentes ont fourni de bonnes explications sur extern.

Mais je veux ajouter un point important.

Vous parlez de extern en C++, pas en C, et je ne sais pas pourquoi il n'y a pas de réponse mentionnant le cas où extern est accompagné de const en C++.

En C++, une variable const a une liaison interne par défaut (contrairement à C).

Ce scénario provoquera une erreur de liaison :

Source 1 :

const int global = 255; //mauvaise façon de définir une variable constante globale en C++

Source 2 :

extern const int global; //déclaration

Il faut que ce soit comme ça :

Source 1 :

extern const int global = 255; //définition d'une variable constante globale en C++

Source 2 :

extern const int global; //déclaration

3 votes

Pourquoi est-ce une erreur alors que cela fonctionne en C++ sans inclure "extern" dans la partie définition ?

1 votes

Je ne semble pas rencontrer cette erreur de liaison dans Visual Studio avec Visual Micro. Que me manque-t-il?

1 votes

@lartist93 @Craig.Feied Je pense que vous devriez vérifier à nouveau attentivement. Même si le compilateur ne signale pas d'erreur de liaison, pourriez-vous vérifier que les deux objets dans les deux sources sont les mêmes sans le extern dans la définition ? Vous pourriez le faire en imprimant la valeur de global dans la source 2.

17voto

Marlon Points 11528

Ceci est utile lorsque vous voulez avoir une variable globale. Vous définissez les variables globales dans un fichier source, et les déclarez extern dans un fichier d'en-tête afin que tout fichier incluant ce fichier d'en-tête voie ensuite la même variable globale.

0 votes

De toute façon, cela ne semble pas très OOP, je les mettrais dans une classe singleton... ou une fonction renvoyant une valeur locale statique...

0 votes

@RzR ajouterait plus d'informations?

0voto

Crazy Eddie Points 23778

Lorsque vous avez des variables globales, vous devez les déclarer extern dans toute unité de traduction où elles ne sont pas définies, sinon vous obtiendrez des définitions multiples. Ceci est bien sûr à éviter car les variables globales ne sont généralement pas recommandées.

Lorsque vous écrivez en C et que vous voulez permettre aux utilisateurs de C++ d'utiliser votre bibliothèque, vous déclarez vos éléments dans un bloc extern "C" {}. Comme le C est nul, vous ne devriez pas en avoir besoin non plus.

Et puis il y a les modèles extern, qui ne font plus partie de la norme.

Enfin, il y a la déclaration d'une instantiation de modèle qui se produit ailleurs et que vous voulez lier plutôt que d'en créer une nouvelle. Vous déclarez également ceux-ci en extern. Cela peut être occasionnellement utile... peut-être... je ne l'ai jamais fait.

Je pense que je peux compter sur une main le nombre de fois où j'ai eu besoin de "extern" en C++ car j'ai tendance à éviter toutes les constructions dans lesquelles il est nécessaire.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X