Quando usare extern in C ++


399

Sto leggendo "Pensa in C ++" e ho appena introdotto la externdichiarazione. Per esempio:

extern int x;
extern float y;

Penso di aver capito il significato (dichiarazione senza definizione), ma mi chiedo quando si rivela utile.

Qualcuno può fornire un esempio?


1
Ho dovuto fornire una definizione externin diverse occasioni. Gli strumenti Microsoft hanno prodotto un errore di collegamento per i simboli mancanti quando sono state definite solo le tabelle in un altro file di origine. Il problema era che la tabella era conste il compilatore C ++ lo aveva promosso staticnell'unità di traduzione. Vedi, per esempio, ariatab.cppe kalynatab.cpp.
jw

2
E penso che la risposta di Nik sia quella corretta perché è l'unico che sembra aver risposto a una domanda C ++. Sembra che tutti gli altri abbiano passato a una domanda in C.
jw

Risposte:


520

Ciò risulta utile quando si hanno variabili globali. Dichiara l' esistenza di variabili globali in un'intestazione, in modo che ogni file di origine che include l'intestazione sia a conoscenza di esso, ma devi solo "definirlo" una volta in uno dei tuoi file di origine.

Per chiarire, usare extern int x;dice al compilatore che un oggetto di tipo intchiamato xesiste da qualche parte . Non è compito dei compilatori sapere dove esiste, deve solo conoscere il tipo e il nome in modo da sapere come usarlo. Una volta compilati tutti i file di origine, il linker risolverà tutti i riferimenti xall'unica definizione che trova in uno dei file di origine compilati. Affinché funzioni, la definizione della xvariabile deve avere quello che viene chiamato "collegamento esterno", il che significa sostanzialmente che deve essere dichiarato al di fuori di una funzione (in quello che di solito viene chiamato "ambito del file") e senza la staticparola chiave.

intestazione:

#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

fonte 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();
}

fonte 2:

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

void print_global_x()
{
    //print global_x here:
    std::cout << global_x << std::endl;
}

15
Grazie. Quindi, se dichiaro una variabile globale in un file di intestazione senza la parola chiave extern, i file di origine che includono l'intestazione non la vedono?
Aslan986,

23
non dovresti dichiarare variabili globali in un'intestazione, perché quando 2 file includono lo stesso file di intestazione, non si collegherà (il linker emetterà un errore sul "simbolo duplicato")
kuba

63
@ Aslan986: No, succede qualcosa di peggio. Ogni file di origine che include l'intestazione avrà una propria variabile, quindi ogni file di origine verrà compilato in modo indipendente ma il linker si lamenterà perché due file di origine avranno gli stessi identificatori globali.
dreamlax,

7
Quando non usi la parola "extern", ora esiste la variabile. Quando usi "extern", è un "ehi, questo var è altrove". Mi dispiace non rispondere se si tratta di una definizione o dichiarazione, dal momento che mi confondo sempre su questi due.
Kuba,

3
@CCJ: la protezione include funziona solo per il file sorgente che lo include. Impedisce che la stessa intestazione venga inclusa due volte nello stesso file di origine (nel caso in cui anche altre intestazioni lo includano, ecc.). Quindi, anche con le guardie di inclusione, ogni file sorgente che include l'intestazione avrà ancora una propria definizione.
dreamlax,

172

È utile quando condividi una variabile tra alcuni moduli. Lo definisci in un modulo e usi extern negli altri.

Per esempio:

in file1.cpp:

int global_int = 1;

in file2.cpp:

extern int global_int;
//in some function
cout << "global_int = " << global_int;

39
Questa risposta è più corretta di quella accettata, in quanto non utilizza il file di intestazione e afferma chiaramente che è utile solo quando si condivide tra pochi moduli. Per applicazioni più grandi è meglio usare ad esempio una classe ConfigManager.
Zac,

1
C'è qualche gotchas quando sono coinvolti gli spazi dei nomi, global_intè nello spazio dei nomi globale, se dovessi usarlo in file2.cpp in qualche sezione dello spazio dei nomi dovrei farlo correttamente? vale a direnamespace XYZ{ void foo(){ ::global_int++ } };
jxramos,

8
@Zac: D'altra parte, non dichiarando una variabile globale in un'intestazione, hai inavvertitamente reso molto più difficile determinare dove è effettivamente definita. Di solito se vedi una variabile globale dichiarata in abc.h, ci sono buone probabilità che venga definita in abc.cpp. Un buon IDE aiuterà sempre, ma un codice ben organizzato è sempre una soluzione migliore.
dreamlax,

senza externin file2.cpp, è ancora possibile accedere a global_intdopo l'inclusione. perché ho bisogno di averlo?
TomSawyer il

62

Riguarda il collegamento .

Le risposte precedenti hanno fornito buone spiegazioni in merito extern.

Ma voglio aggiungere un punto importante.

Ti chiedi externin C ++ e non in C e non so perché non vi sia alcuna risposta menzionando il caso quando externviene fornito constin C ++.

In C ++, una constvariabile ha un collegamento interno per impostazione predefinita (non come C).

Quindi questo scenario porterà all'errore di collegamento :

Fonte 1:

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

Fonte 2:

extern const int global; //declaration

Deve essere così:

Fonte 1:

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

Fonte 2:

extern const int global; //declaration

2
Perché è un errore mentre funziona in c ++ senza includere 'extern' nella parte della definizione?
Chief Shifter,

1
Non mi sembra di riscontrare questo errore di collegamento in VIsual Studio con Visual Micro. Cosa mi sto perdendo?
Craig

1
@ lartist93 @ Craig.Feied Credo che potrebbe essere necessario ricontrollare attentamente. Anche nel caso in cui il compilatore non informi l'errore di collegamento, potresti verificare che entrambi gli oggetti in entrambi i sorgenti siano uguali senza externper definizione? Puoi farlo stampando il valore di globalnella fonte 2.
Trevor,

3
Conferma, in MSVS 2018 non v'è un errore di collegamento, se externviene omesso in const int global = 255;.
Evg

13

Ciò è utile quando si desidera avere una variabile globale. Definisci le variabili globali in alcuni file di origine e le dichiari esterne in un file di intestazione in modo che tutti i file che includono quel file di intestazione vedranno la stessa variabile globale.


Comunque questo non suona molto OOP, li metterei in una classe singleton ... o una funzione che restituisce un valore statico locale ...
RzR
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.