Je pense que la limitation que vous avez envisagée n’est pas liée à la sémantique (pourquoi changer quelque chose si l’initialisation est définie dans le même fichier?), Mais plutôt au modèle de compilation C ++ qui, pour des raisons de compatibilité ascendante, ne peut pas être changé facilement soit devenir trop complexe (prendre en charge simultanément un nouveau modèle de compilation et le modèle existant), soit ne pas permettre de compiler le code existant (en introduisant un nouveau modèle de compilation et en supprimant le modèle existant).
Le modèle de compilation C ++ provient de celui de C, dans lequel vous importez des déclarations dans un fichier source en incluant des fichiers (en-tête). De cette manière, le compilateur voit exactement un gros fichier source, contenant tous les fichiers inclus et tous les fichiers inclus à partir de ces fichiers, de manière récursive. Cela présente un gros avantage pour OMI, à savoir que cela facilite la mise en œuvre du compilateur. Bien sûr, vous pouvez écrire n’importe quoi dans les fichiers inclus, c’est-à-dire à la fois des déclarations et des définitions. Il est recommandé de placer les déclarations dans les fichiers d’en-tête et les définitions dans les fichiers .c ou .cpp.
D'autre part, il est possible d'avoir un modèle de compilation dans lequel le compilateur sait très bien s'il importe la déclaration d'un symbole global défini dans un autre module ou s'il compile la définition d'un symbole global fourni par le module actuel . Ce n'est que dans ce dernier cas que le compilateur doit mettre ce symbole (par exemple une variable) dans le fichier objet actuel.
Par exemple, dans GNU Pascal, vous pouvez écrire une unité a
dans un fichier a.pas
comme celui-ci:
unit a;
interface
var MyStaticVariable: Integer;
implementation
begin
MyStaticVariable := 0
end.
où la variable globale est déclarée et initialisée dans le même fichier source.
Ensuite, vous pouvez avoir différentes unités qui importent a et utilisent la variable globale
MyStaticVariable
, par exemple une unité b ( b.pas
):
unit b;
interface
uses a;
procedure PrintB;
implementation
procedure PrintB;
begin
Inc(MyStaticVariable);
WriteLn(MyStaticVariable)
end;
end.
et une unité c ( c.pas
):
unit c;
interface
uses a;
procedure PrintC;
implementation
procedure PrintC;
begin
Inc(MyStaticVariable);
WriteLn(MyStaticVariable)
end;
end.
Enfin, vous pouvez utiliser les unités b et c dans un programme principal m.pas
:
program M;
uses b, c;
begin
PrintB;
PrintC;
PrintB
end.
Vous pouvez compiler ces fichiers séparément:
$ gpc -c a.pas
$ gpc -c b.pas
$ gpc -c c.pas
$ gpc -c m.pas
et ensuite produire un exécutable avec:
$ gpc -o m m.o a.o b.o c.o
et lancez-le:
$ ./m
1
2
3
L'astuce ici est que lorsque le compilateur voit une directive uses dans un module de programme (par exemple, utilise a dans b.pas), il n'inclut pas le fichier .pas correspondant, mais recherche un fichier .gpi, c'est-à-dire un fichier pré-compilé. fichier d'interface (voir la documentation ). Ces .gpi
fichiers sont générés par le compilateur avec les .o
fichiers lors de la compilation de chaque module. Le symbole global MyStaticVariable
n’est donc défini qu’une fois dans le fichier objet a.o
.
Java fonctionne de la même manière: lorsque le compilateur importe une classe A dans la classe B, il examine le fichier de classe pour A et n’a pas besoin de ce fichier A.java
. Ainsi, toutes les définitions et initialisations de la classe A peuvent être placées dans un fichier source.
Pour revenir à C ++, la raison pour laquelle vous devez définir des membres de données statiques dans un fichier séparé est davantage liée au modèle de compilation C ++ qu'aux limitations imposées par l'éditeur de liens ou d'autres outils utilisés par le compilateur. En C ++, importer des symboles signifie construire leur déclaration dans l'unité de compilation en cours. Ceci est très important, entre autres choses, à cause de la manière dont les modèles sont compilés. Mais cela implique que vous ne pouvez / ne devez définir aucun symbole global (fonctions, variables, méthodes, membres de données statiques) dans un fichier inclus, sinon ces symboles pourraient être définis de manière multiple dans les fichiers d'objet compilés.