web-dev-qa-db-fra.com

Inclure dans le fichier d'en-tête, par rapport à forward-declare et inclure dans .cpp

J'ai une classe B et je souhaite appeler les membres de la classe A. Ainsi:

1.

//A.h    
class B; 
class A 
{ 
private:
    B* m_p; 
}; 

//a.cpp
#include "B.h"

2.

// A.h
#include "B.h"

class A 
{ 
private: 
    B * impl_; 
}; 

quelle voie est la meilleure et est-ce que ces deux choses sont similaires quand un petit projet avec peu de dépendance implique?

24
colddie

Votre première façon de faire signifie que, dans a.h, le existence de class B est connu, mais pas sa définition . Cela limite ce que vous pouvez faire avec B dans a.h. Par exemple, vous pouvez avoir des variables de type B *, mais pas des variables de type B (car pour une déclaration d'une variable de type B, le compilateur doit pouvoir visualiser la définition complète de B). De même, si vous avez des variables de type B *, vous ne pouvez pas déréférencer le pointeur (car pour cela également, la définition de B doit être connue).

Par conséquent, votre deuxième choix - qui ne pose pas ces problèmes - est préférable, et c’est ce que la plupart des gens utilisent la plupart du temps.

Ce ne sont que des cas particuliers dans lesquels la première méthode peut être utile. Par exemple:

  • Si les fichiers .h comprennent mutuellement (mais vous risquez alors de rencontrer un certain nombre de problèmes supplémentaires, également en ce qui concerne include-gardes; ceci est généralement difficile et à éviter);
  • Si b.h est extrêmement volumineux et complexe, évitez de l’inclure autant que possible car cela ralentirait le processus de compilation.
28
jogojapan

Votre première méthode est une déclaration en aval. Votre seconde inclut en fait la classe B. 

Quand utiliser l'un sur l'autre?

Utilisez le premier quand:

  • Dans la definition de A, vous n’avez qu’un pointeur sur B, c’est-à-dire que vous n’êtes pas membre de B.
  • Vous n’appelez jamais une fonction de B à partir de la définition de A. (c’est-à-dire que tous les appels aux fonctions membres de B ont lieu dans le fichier .cpp où vous implémentez les fonctions membres de A).
  • Vous vous attendez à ce que l'interface ou la taille de la classe B change fréquemment, mais pas à l'interface A. Ainsi, si B change, seul le contenu de a.cpp est recompilé, mais ah (et les autres fichiers incluant ah) ne doit pas être modifié. .

Utilisez le second quand:

  • Vous devez connaître le size de B. Le compilateur calcule la taille d'une classe en utilisant sa définition de classe et la taille de tous ses membres. Par exemple, si la classe A a un membre de type B, pour calculer la taille de A, le compilateur doit connaître la taille de B; pour connaître la taille de B, vous devez inclure b.h .
    • Vous devez appeler des fonctions de classe B. Pour savoir si vous appelez des fonctions existantes, le compilateur doit connaître l'interface de la classe B, c'est-à-dire que vous devez inclure b.h.
14
maditya

Réponse: 1.
Regardez http://www.umich.edu/~eecs381/handouts/handouts.html

Directives concernant les fichiers d'en-tête C

Lignes directrices sur les fichiers d'en-tête C++ (par David Kieras, département EECS, Université du Michigan) dit:

Directive n ° 10. Si une déclaration incomplète d'un type X suffira, utilisez il au lieu de # incluant son en-tête X.h. Si une autre structure ou classe Le type X apparaît uniquement en tant que pointeur ou type de référence dans le contenu de un fichier d’en-tête, vous ne devriez pas inclure #include X.h, mais simplement placer un déclaration incomplète de X (également appelée déclaration "en aval") près de le début du fichier d'en-tête, comme dans: class X; Voir le document à distribuer Déclarations incomplètes pour plus de discussion sur ce puissant et technique précieuse. Notez que la bibliothèque Standard inclut un en-tête des déclarations incomplètes qui suffisent souvent pour le <iostream> bibliothèque, nommée <iosfwd>. #include <iosfwd> chaque fois que possible, parce que le fichier d'en-tête <iostream> est extrêmement volumineux (modèles géants!).

11
Andrey Volk

Déclarez simplement la classe dans l'en-tête de votre classe A.

class B;
0
Steve Seo

La seconde est meilleure. La classe B devient un module que vous incluez à l'aide du fichier .h. Considérons le cas où vous sous-classerez B à l'avenir et que vous mettez à jour A pour utiliser C. Dans le second cas, vous ne remplacez que l'en-tête de #include et de A. Dans le premier cas, vous devez modifier la déclaration anticipée. En outre, dans le second cas, vous définissez plus que le symbole B.

Et comme dans les commentaires, vous devriez utiliser #include "B.h" si le fichier d’en-tête est dans le même répertoire que le reste du code.

0
Bart Friederichs

Eh bien, ce que vous faites s'appelle décleration en aval. La raison pour laquelle vous souhaiteriez cela est si vous avez quelque chose comme la classe A qui utilise la classe B et AUSSI la classe B qui utilise la classe A.

Dans le cas où il n’existe qu’une relation, vous pouvez certainement utiliser votre deuxième choix . Si vous avez besoin de la double utilisation, au moins une de vos classes doit utiliser la décélération avant. 

0
Alon