Wann sollte extern in C ++ verwendet werden?

398

Ich lese "Think in C ++" und es wurde gerade die externDeklaration eingeführt. Zum Beispiel:

extern int x;
extern float y;

Ich glaube, ich verstehe die Bedeutung (Erklärung ohne Definition), aber ich frage mich, wann sie sich als nützlich erweist.

Kann jemand ein Beispiel geben?

Aslan986
quelle
1
Ich musste externmehrmals eine Definition angeben. Microsoft-Tools haben einen Verknüpfungsfehler für fehlende Symbole erzeugt, wenn die Tabellen in einer anderen Quelldatei nur definiert wurden. Das Problem war, die Tabelle war constund der C ++ - Compiler hat sie staticin der Übersetzungseinheit hochgestuft. Siehe zum Beispiel ariatab.cppund kalynatab.cpp.
JWW
2
Und ich denke, Niks Antwort ist die richtige, weil er der einzige ist, der anscheinend eine C ++ - Frage beantwortet hat. Alle anderen scheinen sich einer C-Frage zugewandt zu haben.
JWW

Antworten:

519

Dies ist nützlich, wenn Sie globale Variablen haben. Sie deklarieren das Vorhandensein globaler Variablen in einem Header, sodass jede Quelldatei, die den Header enthält, davon Kenntnis hat. Sie müssen sie jedoch nur einmal in einer Ihrer Quelldateien „definieren“.

Zur Verdeutlichung extern int x;teilt using dem Compiler mit, dass irgendwo ein Objekt vom Typ intaufgerufen xexistiert . Es ist nicht die Aufgabe des Compilers, zu wissen, wo es existiert, es muss nur den Typ und den Namen kennen, damit es weiß, wie man es benutzt. Sobald alle Quelldateien kompiliert wurden, löst der Linker alle Verweise auf die eine Definition auf, die er in einer der kompilierten Quelldateien findet. Damit dies funktioniert, muss die Definition der Variablen eine sogenannte "externe Verknüpfung" haben, was im Grunde bedeutet, dass sie außerhalb einer Funktion (in der Regel "Dateibereich") und ohne das Schlüsselwort deklariert werden muss.xxstatic

Header:

#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

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

Quelle 2:

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

void print_global_x()
{
    //print global_x here:
    std::cout << global_x << std::endl;
}
Dreamlax
quelle
15
Vielen Dank. Wenn ich also eine globale Variable in einer Header-Datei ohne das Schlüsselwort extern deklariere, wird sie in den Quelldateien, die den Header enthalten, nicht angezeigt?
Aslan986
23
Sie sollten keine globalen Variablen in einem Header deklarieren, da dann, wenn 2 Dateien dieselbe Header-Datei enthalten, keine Verknüpfung hergestellt wird (der Linker gibt einen Fehler bezüglich des "doppelten Symbols" aus)
kuba
63
@ Aslan986: Nein, etwas Schlimmeres passiert. Jede Quelldatei, die den Header enthält, hat eine eigene Variable, sodass jede Quelldatei unabhängig kompiliert wird. Der Linker beschwert sich jedoch, da zwei Quelldateien dieselben globalen Bezeichner haben.
Dreamlax
7
Wenn Sie das Wort "extern" nicht verwenden, ist die Variable jetzt vorhanden. Wenn Sie "extern" verwenden, ist es ein "Hey, da ist diese Variable woanders". Es tut mir leid, dass ich nicht antworte, ob es sich um eine Definition oder eine Erklärung handelt, da ich bei diesen beiden immer verwirrt bin.
Kuba
3
@CCJ: Der Include Guard funktioniert nur für die Quelldatei, die ihn enthält. Es verhindert, dass derselbe Header zweimal in derselben Quelldatei enthalten ist (nur für den Fall, dass andere Header ihn ebenfalls enthalten usw.). Selbst mit Include Guards hat jede Quelldatei, die den Header enthält, eine eigene Definition.
Dreamlax
172

Dies ist nützlich, wenn Sie eine Variable für einige Module freigeben. Sie definieren es in einem Modul und verwenden extern in den anderen.

Zum Beispiel:

in file1.cpp:

int global_int = 1;

in file2.cpp:

extern int global_int;
//in some function
cout << "global_int = " << global_int;
MByD
quelle
39
Diese Antwort ist korrekter als die akzeptierte, da sie keine Header-Datei verwendet und klar angibt, dass sie nur nützlich ist, wenn sie zwischen wenigen Modulen geteilt wird. Für größere Anwendungen ist es besser, beispielsweise eine ConfigManager-Klasse zu verwenden.
Zac
1
Gibt es Fallstricke, wenn Namespaces betroffen sind, die global_intsich im globalen Namespace befinden. Wenn ich sie in file2.cpp in einem Namespace-Abschnitt verwenden würde, müsste ich sie korrekt festlegen? dhnamespace XYZ{ void foo(){ ::global_int++ } };
jxramos
8
@Zac: Wenn Sie jedoch keine globale Variable in einem Header deklarieren, haben Sie es versehentlich viel schwieriger gemacht, festzustellen, wo sie tatsächlich definiert ist. Wenn eine globale Variable in deklariert ist abc.h, besteht normalerweise eine gute Chance, dass sie in definiert wird abc.cpp. Eine gute IDE wird immer helfen, aber gut organisierter Code ist immer eine bessere Lösung.
Dreamlax
ohne externin file2.cpp, kann immer noch auf global_intafter include zugreifen . Warum muss ich es haben?
TomSawyer
62

Es geht nur um die Verknüpfung .

Die vorherigen Antworten lieferten gute Erklärungen zu extern.

Aber ich möchte einen wichtigen Punkt hinzufügen.

Sie fragen nach externin C ++ nicht in C , und ich weiß nicht , warum es keine Antwort gibt über den Fall zu erwähnen , wenn externmit kommt constin C ++.

In C ++ verfügt eine constVariable standardmäßig über eine interne Verknüpfung (nicht wie C).

Dieses Szenario führt also zu einem Verknüpfungsfehler :

Quelle 1:

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

Quelle 2:

extern const int global; //declaration

Es muss so sein:

Quelle 1:

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

Quelle 2:

extern const int global; //declaration
Trevor
quelle
2
Warum ist es falsch, während es in C ++ funktioniert, ohne 'extern' in den Definitionsteil aufzunehmen?
Chief Shifter
1
Dieser Verknüpfungsfehler in VIsual Studio mit Visual Micro scheint mir nicht zu begegnen. Was vermisse ich?
Craig.Feied
1
@ lartist93 @ Craig.Feied Ich glaube, Sie müssen möglicherweise noch einmal sorgfältig prüfen. Können Sie überprüfen, ob beide Objekte in beiden Quellen ohne externDefinition identisch sind, auch wenn der Compiler keinen Verknüpfungsfehler anzeigt ? Sie können dies tun, indem Sie den Wert von globalin Quelle 2
ausdrucken
3
Bestätigen Sie , in MSVS 2018 dort ist ein Verbindungsfehler , wenn externin weggelassen const int global = 255;.
Evg
13

Dies ist nützlich, wenn Sie eine globale Variable haben möchten. Sie definieren die globalen Variablen in einer Quelldatei und deklarieren sie extern in einer Header-Datei, sodass jede Datei, die diese Header-Datei enthält, dieselbe globale Variable sieht.

Marlon
quelle
Wie auch immer, das klingt nicht sehr OOP, ich würde sie in eine Singleton-Klasse
einordnen