2010-12-08 31 views
3

J'ai vu du code pour une classe placée dans un C++ séparé, alors que les définitions de méthode sont placées dans le fichier d'en-tête. Ma première expérience OOP est avec Java, dans laquelle toutes les méthodes sont placées dans le fichier de classe, et je préfère réellement cela.Méthodes de placement dans les fichiers .h et .cpp

Est-ce que placer toutes mes méthodes dans un fichier d'en-tête affecte le code d'assembly généré par le compilateur, ou pas?

Et si tel est le cas, est-ce que cela nuit aux performances de placer le code entier d'une classe dans son fichier d'en-tête?

Répondre

2

Il sont quelques raisons valables pour le fractionnement d'en-tête/mise en œuvre et compilation séparée:
1. Cela peut être une exigence d'emploi - par exemple, vous fournir une bibliothèque binaire + tête à quelqu'un, ou votre collègues sont trop conservateurs pour accepter quoi que ce soit d'autre.
2. Son encore nécessaire pour développer de très grands projets (disons,> 10M de source), parce que la reconstruction de l'application entière après chaque modification deviendrait douloureuse. (Mais il devrait encore être correct de compiler quelque chose comme jpeglib ou zlib en un seul module)
3. On pense qu'il est plus facile d'utiliser les fichiers d'en-tête comme référence, pour regarder up fonctions et autres. (Mais normalement son mieux pour écrire une documentation appropriée, contrairement à les en-têtes, des bugs dans la documentation sont moins susceptibles d'affecter votre programme)

En outre, il sont des raisons beaucoup plus pour ne pas l'utiliser plus:
1. Vous seriez aime éviter de maintenir le code en double.
2. Les méthodes de classe n'ont pas besoin de déclarations directes
3. Les modèles ne peuvent être déclarés dans les en-têtes de toute façon que
4.Les cas dans lesquels vous n'avez pas besoin de fonction inline sont en fait assez rares, c'est-à-dire appeler de grandes fonctions plusieurs fois dans une boucle serrée, mais il ya noinline attibutes et PGO pour cela. Sinon, l'inlining améliore la vitesse. Et pour ce qui est du gonflement du code, la plupart des bibliothèques sont déjà énormes.
5. Dans l'ensemble, les programmes compilés en tant que source unique sont plus rapides et plus petits, parce que le compilateur peut faire un meilleur travail.
6. Sans les en-têtes, la source serait souvent deux fois plus petite, et le compilateur serait en mesure de vérifier correctement la syntaxe, donc vous ne pourrez pas lier accidentellement un prototype de fonction cdecl "C" externe à une variable comme mise en œuvre. De manière générale, ce serait plus portable, car les différents éditeurs de liens ont des idées différentes sur la correspondance des noms.
7. Son allocation bizarre mais dynamique est fréquemment utilisée uniquement en raison du style d'en-tête - les dépendances peuvent être résolues automatiquement en définissant tous les détails dans une seule classe, mais les gens préfèrent utiliser des pointeurs vers des déclarations de classe partielle à la place (puis chasse les fuites de mémoire).

Maintenant, quelques points de bonus pour les modules d'objets distincts:
4. Statistiques PGO en gcc sont générés par module d'objet, qui semble être le seul moyen de « référence » quelques différents modes de fonctionnement avec un seul exécutable.
5. Il est possible de compiler différents modules avec différentes options de compilateur pour optimiser la vitesse. Il existe également des extensions de compilateur, mais elles ne sont pas très fiables.
6. Parfois, un compilateur peut faire quelque chose de bizarre à une autre partie du code lorsque vous modifiez quelque chose - mais généralement, il ne peut pas se propager en dehors d'un module d'objet.

6

Le fait est que les programmes C++ complexes sont créés en compilant plusieurs objets, puis en les liant ensemble. Chaque objet résulte typiquement de la compilation d'un fichier d'implémentation (par exemple ".cpp", ".cc", etc.), qui peut inclure directement et indirectement un grand nombre d'en-têtes. Par conséquent, si vous écrivez une bonne classe et mettez le code dans l'en-tête, alors ce code peut être inclus dans plusieurs fichiers objet, puis le compilateur le génère de façon redondante, et plus loin - l'éditeur de liens ne peut pas comparer facilement les versions pour voir si elles sont équivalentes et supprimer les copies redondantes (plus facile si vous utilisez des adresses relatives - "code indépendant de la position" - mais c'est une autre histoire). Voir aussi le commentaire de jalf ci-dessous. Donc, vous ne voulez pas de fonctions différentes hors-ligne dans vos en-têtes. Si elles sont nominalement inline fonctions - en raison de l'utilisation du mot-clé inline ou d'être défini dans une classe - alors le compilateur devra simplement effectuer le travail supplémentaire et s'assurer que toute version hors-ligne est uniquement représentée dans le exécutable Mais, pour les fonctions hors ligne, le fardeau reste au programmeur. En outre, si vous fournissez l'implémentation dans vos en-têtes, elle est compilée de manière redondante pour chaque objet, et toute modification de l'en-tête forcera une recompilation de tous les objets de capacité. Les fonctions hors-ligne dans des objets séparés peuvent être modifiées, cet objet unique recompilé, puis il peut être lié à d'autres objets préexistants pour former un nouvel exécutable. Dans les projets à grande échelle, cela économise BEAUCOUP de temps de compilation.

+1

+1 J'aime beaucoup votre réponse. Mais par curiosité, que pensez-vous des modèles? J'ai vu beaucoup de code de modèle soit inclure des en-têtes "inline" supplémentaires ou être un gâchis géant de méthodes. – GWW

+1

@GWW: les templates sont instanciés pour chaque combinaison de types, ce qui les rend très rapides et puissants mais très dépendants du code client. Tout au long de l'histoire C++ personne n'a trouvé un bon moyen d'éviter d'avoir à exposer "l'implémentation" via les en-têtes. # inclure un autre fichier peut parfois aider à séparer l'API, tout comme les définitions de fonctions après les déclarations de classe/struct, mais les résultats optimaux varient selon plusieurs facteurs (longueur de la fonction, quantité et style de documentation, complexité, nombre/compétences des utilisateurs clients par rapport aux responsables de l'implémentation). It * is * messy :-) –

+0

L'éditeur de liens n'est pas obligé de vérifier si deux instances d'une définition de classe sont identiques (et je ne connais pas un éditeur de liens qui le fait). S'ils ont le même nom, il * suppose * qu'ils sont identiques et supprime l'un ou l'autre. Le fardeau est toujours sur le programmeur pour ne pas violer l'ODR. – jalf

1

Oui, les méthodes placées dans l'en-tête sont en ligne, donc elles sont généralement plus rapides (en particulier les plus courtes).

L'inconvénient principal est que chaque modification de l'en-tête provoquera la recompilation de chaque fichier qui l'inclut.

+0

"les méthodes placées dans l'en-tête sont insérées" - dans un code de programmeur C++ expérimenté, c'est vrai, mais les débutants (par exemple seljuq70) font souvent l'erreur de placer des fonctions non-inline dans les en-têtes. Techniquement, seules les fonctions définies (c'est-à-dire avec leur corps) dans un 'class' /' struct', ou qui spécifient le mot-clé 'inline', sont inline. Ils * doivent être * les seules fonctions de l'en-tête, mais peuvent ne pas l'être. –

+0

@Tony: C'est pourquoi j'ai voté votre réponse plus détaillée. Mais vu qu'ils apprennent généralement que * il vaut mieux avoir beaucoup de fonctions courtes *, c'est suffisant. – ruslik

+0

bien, j'aime votre point de vue là-bas alors ... "fonctions simples dans les en-têtes" est un guide simple pour les débutants à utiliser et pas trop faux - c'est couvert par votre explication recompilation de toute façon ... +1. –