En règle générale, lors de la déclaration d'une classe C ++, il est recommandé de ne placer que la déclaration dans le fichier d'en-tête et de placer l'implémentation dans un fichier source. Cependant, il semble que ce modèle de conception ne fonctionne pas pour les classes de modèles.
Lorsque vous regardez en ligne, il semble y avoir 2 opinions sur la meilleure façon de gérer les classes de modèles:
1. Déclaration complète et mise en œuvre dans l'en-tête.
Ceci est assez simple mais conduit à ce qui, à mon avis, est difficile à maintenir et à modifier des fichiers de code lorsque le modèle devient volumineux.
2. Écrivez l'implémentation dans un fichier d'inclusion de modèle (.tpp) inclus à la fin.
Cela me semble être une meilleure solution mais ne semble pas être largement appliqué. Y a-t-il une raison pour laquelle cette approche est inférieure?
Je sais que le style de code est souvent dicté par des préférences personnelles ou un style hérité. Je démarre un nouveau projet (portage d'un ancien projet C vers C ++) et je suis relativement nouveau dans la conception OO et je souhaite suivre les meilleures pratiques dès le départ.
la source
Réponses:
Lors de l'écriture d'une classe C ++ basée sur des modèles, vous avez généralement trois options:
(1) Mettez la déclaration et la définition dans l'en-tête.
ou
Pro:
Con:
Foo
comme membre, vous devez l'inclurefoo.h
. Cela signifie que la modification de l'implémentation de seFoo::f
propage à la fois dans les fichiers d'en-tête et source.Examinons de plus près l'impact de la reconstruction: pour les classes C ++ non basées sur des modèles, vous placez les déclarations dans .h et les définitions de méthode dans .cpp. De cette façon, lorsque l'implémentation d'une méthode est modifiée, un seul .cpp doit être recompilé. Ceci est différent pour les classes de modèles si le .h contient tout votre code. Jetez un œil à l'exemple suivant:
Ici, la seule utilisation de
Foo::f
est à l'intérieurbar.cpp
. Cependant, si vous modifiez l'implémentation deFoo::f
, les deuxbar.cpp
etqux.cpp
doivent être recompilés. L'implémentation deFoo::f
vies dans les deux fichiers, même si aucune partie deQux
n'utilise directement quoi que ce soitFoo::f
. Pour les grands projets, cela peut bientôt devenir un problème.(2) Mettez la déclaration en .h et la définition en .tpp et incluez-la en .h.
Pro:
Con:
Cette solution sépare la déclaration et la définition de la méthode dans deux fichiers distincts, tout comme .h / .cpp. Cependant, cette approche a le même problème de reconstruction que (1) , car l'en-tête inclut directement les définitions de méthode.
(3) Mettez la déclaration dans .h et la définition dans .tpp, mais n'incluez pas .tpp dans .h.
Pro:
Con:
Foo
membre à une classeBar
, vous devez l'inclurefoo.h
dans l'en-tête. Si vous appelezFoo::f
un .cpp, vous devez également l' inclurefoo.tpp
.Cette approche réduit l'impact de la reconstruction, car seuls les fichiers .cpp qui utilisent vraiment
Foo::f
doivent être recompilés. Cependant, cela a un prix: tous ces fichiers doivent être inclusfoo.tpp
. Prenez l'exemple ci-dessus et utilisez la nouvelle approche:Comme vous pouvez le voir, la seule différence est l'inclusion supplémentaire de
foo.tpp
inbar.cpp
. Cela n'est pas pratique et l'ajout d'un second include pour une classe selon que vous appelez des méthodes semble très moche. Cependant, vous réduisez l'impact de la reconstruction: nebar.cpp
doit être recompilé que si vous modifiez l'implémentation deFoo::f
. Le fichierqux.cpp
n'a pas besoin de recompilation.Sommaire:
Si vous implémentez une bibliothèque, vous n'avez généralement pas besoin de vous soucier de l'impact de la reconstruction. Les utilisateurs de votre bibliothèque récupèrent une version et l'utilisent et l'implémentation de la bibliothèque ne change pas dans le travail quotidien de l'utilisateur. Dans de tels cas, la bibliothèque peut utiliser l'approche (1) ou (2) et c'est juste une question de goût que vous choisissez.
Cependant, si vous travaillez sur une application ou si vous travaillez sur une bibliothèque interne de votre entreprise, le code change fréquemment. Vous devez donc vous soucier de l'impact de la reconstruction. Choisir l'approche (3) peut être une bonne option si vous demandez à vos développeurs d'accepter l'inclusion supplémentaire.
la source
Semblable à l'
.tpp
idée (que je n'ai jamais vue utilisée), nous mettons la plupart des fonctionnalités en ligne dans un-inl.hpp
fichier qui est inclus à la fin du.hpp
fichier habituel .Comme d'autres l'indiquent, cela maintient l'interface lisible en déplaçant l'encombrement des implémentations en ligne (comme les modèles) dans un autre fichier. Nous autorisons certaines interfaces en ligne, mais essayons de les limiter à de petites fonctions, généralement à une seule ligne.
la source
Une pièce pro de la 2ème variante est que vos en-têtes sont plus rangés.
Le problème peut être que la vérification des erreurs IDE en ligne et les liaisons du débogueur peuvent être vissées.
la source
Je préfère grandement l'approche consistant à mettre l'implémentation dans un fichier séparé et à avoir uniquement la documentation et les déclarations dans le fichier d'en-tête.
Peut-être que la raison pour laquelle vous n'avez pas beaucoup utilisé cette approche dans la pratique est que vous n'avez pas cherché aux bons endroits ;-)
Ou - c'est peut-être parce qu'il faut un peu d'effort supplémentaire pour développer le logiciel. Mais pour une bibliothèque de classe, cet effort en vaut bien la peine, à mon humble avis, et se paie dans une bibliothèque beaucoup plus facile à utiliser / lire.
Prenez cette bibliothèque par exemple: https://github.com/SophistSolutions/Stroika/
La bibliothèque entière est écrite avec cette approche et si vous regardez à travers le code, vous verrez à quel point cela fonctionne.
Les fichiers d'en-tête sont à peu près aussi longs que les fichiers d'implémentation, mais ils ne sont remplis que de déclarations et de documentation.
Comparez la lisibilité de Stroika avec celle de votre implémentation std c ++ préférée (gcc ou libc ++ ou msvc). Ceux-ci utilisent tous l'approche d'implémentation en ligne en-tête, et bien qu'ils soient extrêmement bien écrits, à mon humble avis, pas en tant qu'implémentations lisibles.
la source