La déclaration anticipée en C++

Publié dans C / C++ | Marqué avec ,
Share

Un problème de référence croisée / inclusion croisée / cross reference en C++ ? Utilisez la déclaration anticipée ! Ou forward declaration dans la langue de Shakespear !

Qu’est-ce qu’une référence croisée ?

Une référence croisée, ce sont tout simplement 2 classes qui dépendent l’une de l’autre. MyClassA dépend de MyClassB et vice-versa. On devrait alors inclure le header de l’un dans celui de l’autre et le compilateur ne sait pas par quel bout commencer. Ce problème apparemment rarement avec seulement deux classes, mais plus souvent dans un cycle : MyClassA <- MyClassB <- … <-MyClassZ <- MyClassA.

Exemple de référence croisée simple entre 3 classes

La solution

La solution : déclaration anticipée ou forward declaration.

C’est assez simple à mettre en œuvre. Ci-dessous le header de MyClassB. Cette classe utilise MyClassA, mais vous remarquerez que le header n’est pas inclue, on indique simplement que le type MyCLassA est connu.

class MyClassA;
class MyClassB {
//...
}

Ensuite dans le fichier source de MyClassB, là où est réellement utilisé MyClassA, on inclue le header de MyClassA.

#include "MyClassB.h"
#include "MyClassA.h"

MyClassB::MyClassB() {
//...
}

Cela résout le problème de référence croisé et réduira aussi le temps de re-compilation. Pour comprendre, je laisse la place à la FAQ C++ : référence croisée de Developpez.com :

De manière générale les déclarations anticipées sont à utiliser autant que possible, à savoir dès qu’on déclare dans une classe un pointeur ou une référence sur une autre classe. En effet, cela permet aussi de limiter les dépendances entre les fichiers et de réduire considérablement le temps de compilation.

Et ce n’est pas le seul endroit où l’on trouve ce type de remarque, comme le montre les liens en référence. On peut donc utiliser allègrement les forward declaration ! A noter toutefois qu’il ne faut pas utiliser cette méthode pour les cas suivants (lire notamment sur ce forum) :

  • en cas d’attribut de type pointeur,
  • dans l’héritage,
  • lors de la spécification de levée d’exception,
  • lors de la déclaration d’amitié avec une méthode,
  • avec les templates (comme string, vector, etc.),
  • avec les énumérations,
  • pas plus avec les typedefs de template (d’après Guru #34).

Plus d’informations

Laisser un commentaire

Votre adresse e-mail ne sera pas publiée. Les champs obligatoires sont indiqués avec *