Quand utiliser extern en C ++

399

Je lis "Think in C ++" et il vient d'introduire la externdéclaration. Par exemple:

extern int x;
extern float y;

Je pense que je comprends le sens (déclaration sans définition), mais je me demande quand cela se révèle utile.

Quelqu'un peut-il donner un exemple?

Aslan986
la source
1
J'ai dû fournir une définition à externplusieurs reprises. Les outils Microsoft ont généré une erreur de lien pour les symboles manquants lorsque les tables d'un autre fichier source ont été définies uniquement. Le problème était que la table était constet le compilateur C ++ l'a promue staticdans l'unité de traduction. Voir, par exemple, ariatab.cppet kalynatab.cpp.
2017
2
Et je pense que la réponse de Nik est la bonne parce qu'il est le seul qui semble avoir répondu à une question C ++. Tout le monde semble s'être éloigné d'une question C.
2017

Réponses:

520

Cela est 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 qui inclut l'en-tête le sache, mais il vous suffit de le «définir» une fois dans l'un de vos fichiers source.

Pour clarifier, using extern int x;indique au compilateur qu'un objet de type intappelé xexiste quelque part . Ce n'est pas le travail des compilateurs de savoir où il existe, il suffit de connaître le type et le nom pour qu'il sache comment l'utiliser. Une fois que tous les fichiers source ont été compilés, l'éditeur de liens résoudra toutes les références de xla définition qu'il trouve dans l'un des fichiers source compilés. Pour que cela fonctionne, la définition de la xvariable doit avoir ce qu'on appelle un «lien externe», ce qui signifie essentiellement qu'elle doit être déclarée en dehors d'une fonction (à ce qui est généralement appelé «la portée du fichier») et sans le staticmot - clé.

entête:

#ifndef HEADER_H
#define HEADER_H

// any source file that includes this will be able to use "global_x"
extern int global_x;

void print_global_x();

#endif

source 1:

#include "header.h"

// since global_x still needs to be defined somewhere,
// we define it (for example) in this source file
int global_x;

int main()
{
    //set global_x here:
    global_x = 5;

    print_global_x();
}

source 2:

#include <iostream>
#include "header.h"

void print_global_x()
{
    //print global_x here:
    std::cout << global_x << std::endl;
}
dreamlax
la source
15
Je vous remercie. 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 le voient pas?
Aslan986
23
vous ne devez pas déclarer de variables globales dans un en-tête, car alors lorsque 2 fichiers incluent le même fichier d'en-tête, il ne sera pas lié (l'éditeur de liens émettra une erreur concernant le "symbole en double")
kuba
63
@ 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 sera compilé indépendamment mais l'éditeur de liens se plaindra car deux fichiers source auront les mêmes identificateurs globaux.
dreamlax
7
Lorsque vous n'utilisez pas le mot "extern", alors maintenant la variable existe. Lorsque vous utilisez "extern", c'est un "hé il y a ce var ailleurs". Désolé de ne pas répondre s'il s'agit d'une définition ou d'une déclaration, car je suis toujours confus à propos de ces deux.
kuba
3
@CCJ: la garde d'inclusion ne fonctionne que pour le fichier source qui l'inclut. Cela empêche le même en-tête d'être inclus deux fois dans le même fichier source (au cas où d'autres en-têtes l'incluraient également, etc.). Ainsi, même avec les gardes d'inclusion, chaque fichier source qui inclut l'en-tête aura toujours sa propre définition.
dreamlax
172

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

Par exemple:

dans file1.cpp:

int global_int = 1;

dans file2.cpp:

extern int global_int;
//in some function
cout << "global_int = " << global_int;
MByD
la source
39
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'elle n'est utile que lors du partage entre quelques modules. Pour les applications plus grandes, il est préférable d'utiliser par exemple une classe ConfigManager.
Zac
1
Y a-t-il des pièges lorsque les espaces de noms sont impliqués, se global_inttrouve dans l'espace de noms global, si je devais l'utiliser dans file2.cpp dans une section d'espace de noms, je devrais l'étendre correctement? ienamespace XYZ{ void foo(){ ::global_int++ } };
jxramos
8
@Zac: D'un autre côté, en ne déclarant pas une variable globale dans un en-tête, vous avez par inadvertance rendu beaucoup plus difficile de déterminer où elle est réellement définie. Habituellement, si vous voyez 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 sera toujours utile, mais un code bien organisé est toujours une meilleure solution.
dreamlax
sans externdans file2.cpp, peut toujours accéder à global_intafter include. pourquoi je dois l'avoir?
TomSawyer
62

Tout dépend du lien .

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

Mais je veux ajouter un point important.

Vous demandez à propos externde C ++ pas en C et je ne sais pas pourquoi il n'y a pas de réponse mentionnant le cas quand externvient avec constC ++.

En C ++, une constvariable a un lien interne par défaut (pas comme C).

Donc, ce scénario entraînera une erreur de liaison :

Source 1:

const int global = 255; //wrong way to make a definition of global const variable in C++

Source 2:

extern const int global; //declaration

Cela doit être comme ceci:

Source 1:

extern const int global = 255; //a definition of global const variable in C++

Source 2:

extern const int global; //declaration
Trevor
la source
2
Pourquoi est-ce un problème alors qu'il fonctionne en c ++ sans inclure 'extern' dans la partie définition?
Chief Shifter
1
Je ne semble pas rencontrer cette erreur de liaison dans VIsual Studio avec Visual Micro. Qu'est-ce que je rate?
Craig.Feied
1
@ lartist93 @ Craig.Feied Je pense que vous devrez peut-être vérifier à nouveau attentivement. Même si le compilateur n'informe pas d'une erreur de liaison, pourriez-vous vérifier que les deux objets des deux sources sont identiques sans externdéfinition? Vous pouvez le faire en imprimant la valeur de la globalsource 2.
Trevor
3
Confirmez, dans MSVS 2018, il y a une erreur de liaison si externest omis dans const int global = 255;.
Evg
13

Ceci est utile lorsque vous souhaitez 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 qui comprend ce fichier d'en-tête voit alors la même variable globale.

Marlon
la source
Quoi qu'il en soit, cela ne sonne pas très OOP, je les mettrais dans une classe singleton ... ou une fonction renvoyant une valeur statique locale ...
RzR