Bien que vous puissiez inclure des .cpp
fichiers comme vous l'avez mentionné, c'est une mauvaise idée.
Comme vous l'avez mentionné, les déclarations appartiennent aux fichiers d'en-tête. Ceux-ci ne posent aucun problème lorsqu'ils sont inclus dans plusieurs unités de compilation car ils n'incluent pas d'implémentations. L'inclusion de plusieurs fois la définition d'une fonction ou d'un membre de classe causera normalement un problème (mais pas toujours) car l'éditeur de liens sera confus et générera une erreur.
Ce qui devrait arriver, c'est que chaque .cpp
fichier comprend des définitions pour un sous-ensemble du programme, comme une classe, un groupe de fonctions organisé de manière logique, des variables statiques globales (à utiliser avec modération le cas échéant), etc.
Chaque unité de compilation ( .cpp
fichier) comprend alors toutes les déclarations dont elle a besoin pour compiler les définitions qu'elle contient. Il garde une trace des fonctions et des classes qu'il référence mais ne contient pas, de sorte que l'éditeur de liens peut les résoudre plus tard lorsqu'il combine le code objet dans un exécutable ou une bibliothèque.
Exemple
Foo.h
-> contient une déclaration (interface) pour la classe Foo.
Foo.cpp
-> contient la définition (implémentation) de la classe Foo.
Main.cpp
-> contient la méthode principale, le point d'entrée du programme. Ce code instancie un Foo et l'utilise.
Les deux Foo.cpp
et Main.cpp
doivent inclure Foo.h
. Foo.cpp
il a besoin parce qu'il définit le code qui sauvegarde l'interface de classe, donc il a besoin de savoir ce que cette interface est. Main.cpp
en a besoin car il crée un Foo et invoque son comportement, il doit donc savoir quel est ce comportement, la taille d'un Foo en mémoire et comment trouver ses fonctions, etc. mais il n'a pas encore besoin de l'implémentation réelle.
Le compilateur générera à Foo.o
partir de Foo.cpp
ce qui contient tout le code de la classe Foo sous forme compilée. Il génère également Main.o
qui inclut la méthode principale et des références non résolues à la classe Foo.
Vient maintenant l'éditeur de liens, qui combine les deux fichiers objets Foo.o
et Main.o
en un fichier exécutable. Il voit les références Foo non résolues dans Main.o
mais voit que Foo.o
contient les symboles nécessaires, il « relie les points » pour ainsi dire. Un appel de fonction Main.o
est maintenant connecté à l'emplacement réel du code compilé de façon à l' exécution, le programme peut sauter au bon endroit.
Si vous aviez inclus le Foo.cpp
fichier dans Main.cpp
, il y aurait deux définitions de la classe Foo. L'éditeur de liens verrait cela et dirait "Je ne sais pas lequel choisir, c'est donc une erreur." L'étape de compilation réussirait, mais pas la liaison. (À moins que vous ne compiliez pas, Foo.cpp
mais pourquoi est-ce dans un .cpp
fichier séparé ?)
Enfin, l'idée des différents types de fichiers est sans rapport avec un compilateur C / C. Il compile « fichiers texte » qui contiennent un code valide , espérons pour la langue souhaitée. Parfois , il peut être en mesure de dire la langue en fonction de l'extension du fichier. Par exemple, compiler un .c
fichier avec aucune option de compilateur et il assumera C, tandis qu'une .cc
ou l' .cpp
extension raconteraient à assumer C ++. Cependant, je peux facilement dire à un compilateur de compiler un fichier .h
ou même .docx
en C ++, et il émettra un .o
fichier object ( ) s'il contient du code C ++ valide au format texte brut. Ces extensions sont plus au profit du programmeur. Si je vois Foo.h
et Foo.cpp
, je suppose tout de suite que la première contient la déclaration de la classe et la seconde contient la définition.