Compiler et relier plusieurs fichiers en C++
un de mes amis" non programmeur " a récemment décidé de faire un programme C++ pour résoudre un problème mécanique compliqué.
il a écrit chaque fonction séparément .fichier cpp, puis inclus dans le fichier source principal, quelque chose comme ceci:
main.rpc:
#include "function1.cpp"
#include "function2.cpp"
...
int main()
{
...
}
il alors compilé le code, avec une seule ligne gcc:
g++ main.cpp // took about 2 seconds
maintenant, je sais que cela devrait marcher, mais je ne suis pas sûr si inclure .rpc fichiers directement dans le programme principal est une bonne idée. J'ai vu le schéma suivant plusieurs fois, où tous les prototypes de fonction aller dans un fichier d'en-tête avec le mot-clé extern, comme ceci:
funcs.h:
extern void function1(..);
extern void function2(..);
...
main.rpc:
...
#include "funcs.h"
...
& compilation avec:
g++ -c function1.cpp
g++ -c function2.cpp
...
g++ -c main.cpp
g++ -o final main.o function1.o function2.o ...
je pense que ce schéma est meilleur (avec un makefile, bien sûr). Quelles raisons puis-je donner à mon ami de convaincre de lui de la sorte?
2 réponses
la principale raison pour laquelle les gens compilent objet par objet est de gagner du temps. Les changements de code localisés de haut niveau nécessitent souvent la compilation d'un seul objet et d'un relink, ce qui peut être plus rapide. (Compiler trop d'objets qui dessinent des tas d'en-têtes, ou instancier de façon redondante les mêmes gabarits, peut en fait être plus lent quand un changement dans le code commun déclenche une recompilation plus complète).
Si le projet est si petit qu'il peut être compilé en 2 secondes, puis il n'y a pas beaucoup de réel profitez de l'approche traditionnelle, bien que faire ce qui est attendu peut faire gagner du temps au développeur - comme le vôtre et le nôtre ici :-). Équilibrer cela, maintenir un makefile prend du temps aussi, bien que vous puissiez finir par le faire de toute façon afin de capturer commodément les répertoires d'inclusion, les bibliothèques, les commutateurs de compilateurs, etc.
implications réelles pour le code écrit / généré:
- les fichiers cpp contiennent normalement leurs propres en-têtes, ce qui permet de vérifier que les le contenu de l'en-tête peut être utilisé indépendamment par d'autres codes clients: mettez tout ensemble et l'espace de noms est déjà "contaminé" par des includes provenant d'en-têtes/fichiers d'implémentation antérieurs
- le compilateur peut optimiser mieux quand tout est dans une unité de traduction (+1 pour le commentaire de leppie, faites de même...)
- les variables statiques non-membres et les espaces de noms anonymes sont privés à l'Unité de traduction, donc inclure plusieurs cpps signifie partager ceux-ci autour, pour le meilleur ou pour le pire. le pire (+1 pour Alexandre :-))
- disons qu'un fichier cpp définit une fonction ou une variable qui n'est pas mentionnée dans son en-tête et qui pourrait même être dans un espace de noms anonyme ou statique: le code plus tard dans l'Unité de traduction pourrait l'appeler librement sans avoir besoin de hacker leur propre déclaration forward (c'est mauvais - si la fonction était destinée à être appelée en dehors de son propre cpp alors il aurait dû être dans l'en-tête et un symbole exposé extérieurement dans son unité de traduction objet)
BTW-en C++ Vos en-têtes peuvent déclarer des fonctions sans utiliser explicitement le extern
mot clé, et c'est normal de le faire.
la raison pour le second style est parce que chacun .le fichier cpp peut être traité séparément, avec ses propres classes, variables globales, ect.. sans risque de conflit.
il est également plus facile dans les IDEs qui lient automatiquement tous les .fichiers cpp (comme MSVC).