Kiedy używać extern w C ++

398

Czytam „Think in C ++” i właśnie wprowadziłem externdeklarację. Na przykład:

extern int x;
extern float y;

Myślę, że rozumiem znaczenie (deklaracja bez definicji), ale zastanawiam się, kiedy okaże się przydatne.

Czy ktoś może podać przykład?

Aslan986
źródło
1
externKilkakrotnie musiałem podać definicję . Narzędzia Microsoft spowodowały błąd łącza dla brakujących symboli, gdy tabele w innym pliku źródłowym zostały zdefiniowane. Problem polegał na tym, że tabela była, consta kompilator C ++ promował ją staticw jednostce tłumaczeniowej. Zobacz na przykład ariatab.cppi kalynatab.cpp.
jww
2
I myślę, że odpowiedź Nik jest poprawna, ponieważ wydaje się, że jako jedyny odpowiedział na pytanie w C ++. Wygląda na to, że wszyscy pozostali dygresję do pytania C.
jww

Odpowiedzi:

519

Jest to przydatne, gdy masz zmienne globalne. W nagłówku deklarujesz istnienie zmiennych globalnych, tak aby każdy plik źródłowy zawierający nagłówek wiedział o nim, ale wystarczy „zdefiniować” go tylko raz w jednym ze swoich plików źródłowych.

Aby to wyjaśnić, użycie extern int x;informuje kompilator, że obiekt typu o intnazwie xjest gdzieś istnieje . Nie jest zadaniem kompilatora wiedzieć, gdzie on istnieje, wystarczy znać typ i nazwę, aby wiedzieć, jak go używać. Po skompilowaniu wszystkich plików źródłowych konsolidator rozwiąże wszystkie odwołania xdo jednej definicji, którą znajdzie w jednym ze skompilowanych plików źródłowych. Aby zadziałało, definicja xzmiennej musi mieć tak zwane „powiązanie zewnętrzne”, co w zasadzie oznacza, że ​​należy ją zadeklarować poza funkcją (w tak zwanym „zakresie plików”) i bez staticsłowa kluczowego.

nagłówek:

#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

źródło 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();
}

źródło 2:

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

void print_global_x()
{
    //print global_x here:
    std::cout << global_x << std::endl;
}
dreamlax
źródło
15
Dziękuję Ci. Więc jeśli zadeklaruję zmienną globalną w pliku nagłówkowym bez słowa kluczowego extern, pliki źródłowe zawierające nagłówek nie zobaczą?
Aslan986
23
nie należy deklarować zmiennych globalnych w nagłówku, ponieważ wtedy, gdy 2 pliki zawierają ten sam plik nagłówka, nie zostanie ono połączone (linker wyemituje błąd dotyczący „duplikatu symbolu”)
kuba
63
@ Aslan986: Nie, dzieje się coś gorszego. Każdy plik źródłowy zawierający nagłówek będzie miał własną zmienną, więc każdy plik źródłowy będzie się kompilował niezależnie, ale linker narzeka, ponieważ dwa pliki źródłowe będą miały te same identyfikatory globalne.
dreamlax
7
Jeśli nie używasz słowa „extern”, to teraz zmienna istnieje. Kiedy używasz „extern”, jest to „hej, ten wariant jest gdzie indziej”. Przepraszam, że nie odpowiedziałem, czy jest to definicja czy deklaracja, ponieważ zawsze się mylę co do tych dwóch.
kuba
3
@CCJ: osłona włączająca działa tylko dla dołączonego pliku źródłowego. Zapobiega włączaniu tego samego nagłówka dwukrotnie w tym samym pliku źródłowym (na wypadek, gdyby inne nagłówki również go zawierały itp.). Tak więc nawet przy włączonych strażnikach każdy plik źródłowy zawierający nagłówek nadal będzie miał własną definicję.
dreamlax
172

Jest to przydatne, gdy udostępniasz zmienną między kilkoma modułami. Zdefiniuj go w jednym module, a w innych użyj zewnętrznego.

Na przykład:

w pliku1.cpp:

int global_int = 1;

w pliku2.cpp:

extern int global_int;
//in some function
cout << "global_int = " << global_int;
MByD
źródło
39
Ta odpowiedź jest bardziej poprawna niż zaakceptowana, ponieważ nie korzysta z pliku nagłówkowego i wyraźnie stwierdza, że ​​jest użyteczna tylko podczas udostępniania między kilkoma modułami. W przypadku większych aplikacji lepiej jest użyć na przykład klasy ConfigManager.
Zac
1
Czy są jakieś luki, gdy global_intw grę wchodzą przestrzenie nazw, jest w globalnej przestrzeni nazw, gdybym używał jej w pliku file2.cpp w jakiejś sekcji przestrzeni nazw, to musiałbym ją poprawnie zakreślić? tj.namespace XYZ{ void foo(){ ::global_int++ } };
jxramos
8
@Zac: Z drugiej strony, nie deklarując zmiennej globalnej w nagłówku, nieumyślnie utrudniłeś ustalenie, gdzie jest ona faktycznie zdefiniowana. Zwykle jeśli widzisz deklarowaną zmienną globalną abc.h, istnieje duża szansa, że ​​zostanie zdefiniowana abc.cpp. Dobre IDE zawsze pomoże, ale dobrze zorganizowany kod jest zawsze lepszym rozwiązaniem.
dreamlax
bez externpliku2.cpp nadal można uzyskać dostęp do global_intpo dołączeniu. dlaczego muszę to mieć?
TomSawyer
62

Chodzi o połączenie .

Poprzednie odpowiedzi dostarczyły dobrych wyjaśnień na temat extern.

Ale chcę dodać ważną kwestię.

Pytasz externw C ++, a nie w C i nie wiem, dlaczego nie ma odpowiedzi na temat przypadku, gdy externjest constw C ++.

W C ++ constzmienna ma domyślnie wewnętrzne powiązanie (nie tak jak C).

Więc ten scenariusz doprowadzi do błędu połączenia :

Źródło 1:

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

Źródło 2:

extern const int global; //declaration

Musi być tak:

Źródło 1:

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

Źródło 2:

extern const int global; //declaration
Trevor
źródło
2
Dlaczego to źle, gdy działa w c ++ bez uwzględnienia „extern” w części definicji?
Chifter Shifter,
1
Wydaje mi się, że ten błąd łączenia w VIsual Studio z Visual Micro nie występuje. czego mi brakuje?
Craig.Feied
1
@ lartist93 @ Craig.Feied Uważam, że może zajść potrzeba ponownego sprawdzenia. Nawet jeśli kompilator nie informuje o błędzie łączenia, czy możesz sprawdzić, czy oba obiekty w obu źródłach są takie same bez externdefinicji? Możesz to zrobić, drukując wartość globalw źródle 2.
Trevor
3
Potwierdź, w MSVS 2018 nie jest łącząca błąd, jeśli externjest pominięta const int global = 255;.
Ewg.
13

Jest to przydatne, gdy chcesz mieć zmienną globalną. Definiujesz zmienne globalne w jakimś pliku źródłowym i deklarujesz je jako zewnętrzne w pliku nagłówkowym, aby każdy plik zawierający ten plik nagłówkowy zobaczył tę samą zmienną globalną.

Marlon
źródło
W każdym razie nie brzmi to zbyt dobrze OOP, umieściłbym je w klasie singletonów ... lub funkcji zwracającej lokalną wartość statyczną ...
RzR