Le mot clé extern est-il vraiment nécessaire?

... #include "test1.h" int main(..) { count << aaa <<endl; } 

aaa est défini dans test1.h , et je n’ai pas utilisé de mot-clé extern, mais je peux toujours faire référence à aaa .

Donc, je doute que l’ extern est vraiment nécessaire?

extern a ses usages. Mais il s’agit principalement de “variables globales” qui sont mal vues. L’idée principale derrière extern est de déclarer des choses avec un lien externe. En tant que tel, c’est un peu l’opposé de static . Mais la liaison externe est dans de nombreux cas la liaison par défaut, vous n’avez donc pas besoin d’ extern dans ce cas. Une autre utilisation de extern est la suivante: il peut transformer des définitions en déclarations. Exemples:

 extern int i; // Declaration of i with external linkage // (only tells the comstackr about the existence of i) int i; // Definition of i with external linkage // (actually reserves memory, should not be in a header file) const int f = 3; // Definition of f with internal linkage (due to const) // (This applies to C++ only, not C. In C f would have // external linkage.) In C++ it's perfectly fine to put // somethibng like this into a header file. extern const int g; // Declaration of g with external linkage // could be placed into a header file extern const int g = 3; // Definition of g with external linkage // Not supposed to be in a header file static int t; // Definition of t with internal linkage. // may appear anywhere. Every translation unit that // has a line like this has its very own t object. 

Vous voyez, c’est plutôt compliqué. Il existe deux concepts orthogonaux: le couplage (externe vs interne) et la question de la déclaration par rapport à la définition. Le mot clé extern peut affecter les deux. En ce qui concerne les liens, c’est le contraire de static . Mais la signification de static est également surchargée et – en fonction du contexte – contrôle ou non le couplage. L’autre est de contrôler la durée de vie des objects (“durée de vie statique”). Mais au niveau mondial, toutes les variables ont déjà une durée de vie statique et certaines personnes ont pensé que ce serait une bonne idée de recycler le mot clé pour contrôler le couplage (c’est juste pour deviner).

La liaison est fondamentalement une propriété d’un object ou d’une fonction déclarée / définie dans “namespace scope”. S’il existe un lien interne, il ne sera pas directement accessible par son nom à partir d’autres unités de traduction. S’il existe un lien externe, il ne doit y avoir qu’une seule définition parmi toutes les unités de traduction (avec des exceptions, voir règle à une définition).

J’ai trouvé que le meilleur moyen d’organiser vos données est de suivre deux règles simples:

  • Déclarez uniquement les objects dans les fichiers d’en-tête.
  • Définissez les éléments dans les fichiers C (ou cpp, mais je vais simplement utiliser C ici pour simplifier).

Par déclarer, je veux dire informer le compilateur que des choses existent, mais ne leur allouez pas de mémoire. Cela inclut typedef , struct , extern , etc.

Par définition, je veux généralement dire “allouer de l’espace pour”, comme int , etc.

Si vous avez une ligne comme:

 int aaa; 

dans un fichier d’en-tête, chaque unité de compilation (définie en tant que stream d’entrée pour le compilateur – le fichier C ainsi que tout ce qu’elle apporte avec #include , de manière récursive) obtiendra sa propre copie. Cela créera des problèmes si vous liez deux fichiers object ayant le même symbole défini (sauf dans certaines circonstances limitées telles que const ).

Une meilleure façon de faire est de définir cette variable aaa dans l’un de vos fichiers C, puis de mettre:

 extern int aaa; 

dans votre fichier d’en-tête.

Notez que si votre fichier d’en-tête n’est inclus que dans un fichier C, cela ne pose pas de problème. Mais, dans ce cas, je n’aurais probablement même pas de fichier d’en-tête. Les fichiers d’en-tête sont, à mon avis, uniquement destinés au partage d’éléments entre les unités de compilation.

Si votre test1.h a la définition de aaa et que vous souhaitez inclure le fichier d’en-tête dans plusieurs unités de traduction, vous rencontrerez plusieurs erreurs de définition, sauf si aaa est constant. Il est préférable de définir aaa dans un fichier cpp et d’append une définition extern dans un fichier d’en-tête qui pourrait être ajouté à d’autres fichiers en tant qu’en-tête.

Règle du pouce pour avoir variable et constante dans le fichier d’en-tête

  extern int a ;//Data declarations const float pi = 3.141593 ;//Constant definitions 

Étant donné que les constantes ont une liaison interne en c ++, toute constante définie dans une unité de traduction ne sera pas visible par les autres unités de traduction, mais ce n’est pas le cas pour les variables dont elles disposent par une liaison externe, c’est-à-dire qu’elles sont visibles par une autre unité de traduction. Placer la définition d’une variable dans un en-tête, partagé dans une autre unité de traduction, conduirait à plusieurs définitions d’une variable, entraînant plusieurs erreurs de définition.

Dans ce cas, extern n’est pas nécessaire. Extern est nécessaire lorsque le symbole est déclaré dans une autre unité de compilation.

Lorsque vous utilisez la directive de pré-traitement #include , le fichier inclus est copié à la place de la directive. Dans ce cas, vous n’avez pas besoin d’ extern car le compilateur sait déjà aaa .

Si aaa n’est pas défini dans une autre unité de compilation, vous n’avez pas besoin d’extern, sinon vous en aurez besoin.