Unités de traduction et liaison

Dans un programme C++, un symbole, par exemple une variable ou un nom de fonction, peut être déclaré n’importe quel nombre de fois dans son étendue. Toutefois, elle ne peut être définie qu’une seule fois. Cette règle est la « règle de définition unique » (ODR). Une déclaration introduit (ou réintroduise) un nom dans le programme, ainsi que suffisamment d’informations pour associer ultérieurement le nom à une définition. Une définition introduit un nom et fournit toutes les informations nécessaires pour la créer. Si le nom représente une variable, une définition crée explicitement le stockage et l’initialise. Une définition de fonction se compose de la signature et du corps de la fonction. Une définition de classe se compose du nom de classe suivi d’un bloc qui répertorie tous les membres de classe. (Les corps des fonctions membres peuvent éventuellement être définis séparément dans un autre fichier.)

L’exemple suivant montre certaines déclarations :

int i;
int f(int x);
class C;

L’exemple suivant montre quelques définitions :

int i{42};
int f(int x){ return x * i; }
class C {
public:
   void DoSomething();
};

Un programme se compose d’une ou plusieurs unités de traduction. Une unité de traduction se compose d’un fichier d’implémentation et de tous les en-têtes qu’il inclut directement ou indirectement. Les fichiers d’implémentation ont généralement une extension de fichier ou .cpp.cxx. Les fichiers d’en-tête ont généralement une extension de .h ou .hpp. Chaque unité de traduction est compilée indépendamment par le compilateur. Une fois la compilation terminée, l’éditeur de liens fusionne les unités de traduction compilées en un seul programme. Les violations de la règle ODR s’affichent généralement en tant qu’erreurs d’éditeur de liens. Les erreurs de l’éditeur de liens se produisent lorsque le même nom est défini dans plusieurs unités de traduction.

En général, la meilleure façon de rendre une variable visible sur plusieurs fichiers consiste à la déclarer dans un fichier d’en-tête. Ajoutez ensuite une #include directive dans chaque .cpp fichier qui nécessite la déclaration. En ajoutant des gardes d’inclure autour du contenu de l’en-tête, vous assurez que les noms qu’un en-tête déclare ne sont déclarés qu’une seule fois pour chaque unité de traduction. Définissez le nom dans un seul fichier d’implémentation.

En C++20, les modules sont introduits comme une alternative améliorée aux fichiers d’en-tête.

Dans certains cas, il peut être nécessaire de déclarer une variable globale ou une classe dans un .cpp fichier. Dans ce cas, vous avez besoin d’un moyen d’indiquer au compilateur et à l’éditeur de liens le type de liaison dont le nom a le nom. Le type de liaison spécifie si le nom de l’objet n’est visible que dans un seul fichier ou dans tous les fichiers. Le concept de liaison s’applique uniquement aux noms globaux. Le concept de liaison ne s’applique pas aux noms déclarés dans une étendue. Une étendue est spécifiée par un ensemble d’accolades englobantes, comme dans les définitions de fonction ou de classe.

Liaison externe ou interne

Une fonction libre est une fonction définie au niveau de l’étendue globale ou de l’espace de noms. Les variables globales non const et les fonctions libres par défaut ont une liaison externe ; elles sont visibles à partir de n’importe quelle unité de traduction dans le programme. Aucun autre objet global ne peut avoir ce nom. Un symbole avec liaison interne ou aucune liaison n’est visible que dans l’unité de traduction dans laquelle elle est déclarée. Lorsqu’un nom a une liaison interne, le même nom peut exister dans une autre unité de traduction. Les variables déclarées dans les définitions de classes ou les corps de fonction n’ont aucune liaison.

Vous pouvez forcer un nom global à avoir une liaison interne en la déclarant explicitement comme static. Cette mot clé limite sa visibilité à la même unité de traduction dans laquelle elle est déclarée. Dans ce contexte, static cela signifie quelque chose de différent de celui appliqué aux variables locales.

Les objets suivants ont une liaison interne par défaut :

  • Objets const
  • Objets constexpr
  • Objets typedef
  • static objets dans l’étendue de l’espace de noms

Pour donner une const liaison externe d’objet, déclarez-le comme extern et affectez-le à une valeur :

extern const int value = 42;

Pour plus d’informations, consultez extern.

Voir aussi

Concepts de base