Ich denke, die Einschränkung, die Sie in Betracht gezogen haben, hängt nicht mit der Semantik zusammen (warum sollte sich etwas ändern, wenn die Initialisierung in derselben Datei definiert wurde?), Sondern mit dem C ++ - Kompilierungsmodell, das aus Gründen der Abwärtskompatibilität nicht einfach geändert werden kann, weil dies der Fall wäre entweder zu komplex werden (gleichzeitig ein neues und ein vorhandenes Kompilierungsmodell unterstützen) oder es nicht zulassen, vorhandenen Code zu kompilieren (indem ein neues Kompilierungsmodell eingeführt und das vorhandene gelöscht wird).
Das C ++ - Kompilierungsmodell stammt aus dem von C, in dem Sie Deklarationen in eine Quelldatei importieren, indem Sie (Header-) Dateien einschließen. Auf diese Weise sieht der Compiler rekursiv genau eine große Quelldatei, die alle enthaltenen Dateien und alle darin enthaltenen Dateien enthält. Dies hat IMO einen großen Vorteil, nämlich dass es den Compiler einfacher zu implementieren macht. Natürlich können Sie alles in die enthaltenen Dateien schreiben, dh sowohl Deklarationen als auch Definitionen. Es ist nur eine gute Praxis, Deklarationen in Headerdateien und Definitionen in C- oder CPP-Dateien abzulegen.
Andererseits ist es möglich, ein Kompilierungsmodell zu haben, in dem der Compiler sehr gut weiß, ob er die Deklaration eines globalen Symbols importiert, das in einem anderen Modul definiert ist , oder ob er die Definition eines globalen Symbols kompiliert, das von bereitgestellt wird das aktuelle Modul . Nur im letzteren Fall muss der Compiler dieses Symbol (zB eine Variable) in die aktuelle Objektdatei einfügen.
In GNU Pascal können Sie beispielsweise eine Unit a
in eine Datei a.pas
wie die folgende schreiben :
unit a;
interface
var MyStaticVariable: Integer;
implementation
begin
MyStaticVariable := 0
end.
Dabei wird die globale Variable in derselben Quelldatei deklariert und initialisiert.
Dann können Sie verschiedene Einheiten haben, die a importieren und die globale Variable verwenden
MyStaticVariable
, z. B. eine Einheit b ( b.pas
):
unit b;
interface
uses a;
procedure PrintB;
implementation
procedure PrintB;
begin
Inc(MyStaticVariable);
WriteLn(MyStaticVariable)
end;
end.
und eine Einheit c ( c.pas
):
unit c;
interface
uses a;
procedure PrintC;
implementation
procedure PrintC;
begin
Inc(MyStaticVariable);
WriteLn(MyStaticVariable)
end;
end.
Schließlich können Sie die Einheiten b und c in einem Hauptprogramm verwenden m.pas
:
program M;
uses b, c;
begin
PrintB;
PrintC;
PrintB
end.
Sie können diese Dateien separat kompilieren:
$ gpc -c a.pas
$ gpc -c b.pas
$ gpc -c c.pas
$ gpc -c m.pas
und dann erstellen Sie eine ausführbare Datei mit:
$ gpc -o m m.o a.o b.o c.o
und führe es aus:
$ ./m
1
2
3
Der Trick dabei ist , dass , wenn der Compiler sieht eine Nutzung in einem Programmmodul - Richtlinie (zB in b.pas verwendet), ist es nicht die entsprechende .pas - Datei enthält, sieht aber für eine .gpi Datei, dh für ein vorkompilierte Schnittstellendatei (siehe Dokumentation ). Diese .gpi
Dateien werden vom Compiler zusammen mit den .o
Dateien generiert, wenn jedes Modul kompiliert wird. Das globale Symbol MyStaticVariable
wird also nur einmal in der Objektdatei definiert a.o
.
Java funktioniert auf ähnliche Weise: Wenn der Compiler dann eine Klasse A in Klasse B importiert, sucht er in der Klassendatei nach A und benötigt die Datei nicht A.java
. So können alle Definitionen und Initialisierungen für Klasse A in einer Quelldatei abgelegt werden.
Zurück zu C ++: Der Grund, warum Sie in C ++ statische Datenelemente in einer separaten Datei definieren müssen, hängt mehr mit dem C ++ - Kompilierungsmodell zusammen als mit Einschränkungen, die durch den Linker oder andere vom Compiler verwendete Tools auferlegt werden. In C ++ bedeutet das Importieren einiger Symbole, dass ihre Deklaration als Teil der aktuellen Kompilierungseinheit erstellt wird. Dies ist unter anderem aufgrund der Art und Weise, wie Vorlagen kompiliert werden, sehr wichtig. Dies impliziert jedoch, dass Sie keine globalen Symbole (Funktionen, Variablen, Methoden, statische Datenelemente) in einer eingeschlossenen Datei definieren können / sollten, da diese Symbole andernfalls in den kompilierten Objektdateien mehrfach definiert sein könnten.