6
votes

Quelqu'un peut-il aider à clarifier la fonction de fichiers d'en-tête?

Je travaille avec C ++ pendant quelques semaines de quelques semaines maintenant, mais le mécanisme derrière les fichiers d'en-tête (ou le lien que je suppose?) Confonde le diable de moi. J'ai eu l'habitude de créer un "Main.h" pour regrouper mes autres fichiers d'en-tête et garder le rang principal.cpp, mais parfois ces fichiers d'en-tête se plaignent de ne pas pouvoir trouver un fichier d'en-tête différent (même s'il est déclaré qu'il est déclaré dans le "main.h"). Je ne l'explique probablement pas très bien alors voici une version abrégée de ce que j'essaie de faire: xxx

- xxx

- xxx

- xxx

- xxx

i gagné 'T Inclure Health.CPP car il est un peu long (mais travaille), il a #include "health.h" .

Quoi qu'il en soit, le compilateur (code :: Les blocs) se plaint que «joueur.h» ne trouve pas les types de «santé» ou «vecteur». Je pensais que si j'utilisais #include "main.h" dans "player.h", il serait capable de trouver les définitions de santé et vecteur sens ils sont inclus dans "Main.H". Je pensais qu'ils seraient trembler de tunnel comme (joueur.h -> main.h -> Health.h). Mais cela ne fonctionnait pas trop bien. Existe-t-il une sorte de diagramme ou de vidéo qui pourrait clarifier la manière dont cela devrait être configuré? Google n'était pas beaucoup d'aide (ni mon livre).


5 commentaires

Cela ne répond pas à votre question, mais vous devriez changer la structure de vecteur à un nom différent. Il va grandir déroutant lorsque vous commencez à utiliser STD :: Vector, et un point dans l'espace 3D n'est pas vraiment un vecteur de toute façon.


Merci, je vais faire ça. Et vous avez raison, il devrait être point ou quelque chose.


En fait, un vecteur est défini par juste un point.


Je ne sais pas ce que tu veux dire spidey. Un vecteur a une ampleur et une direction. Un point serait juste une valeur scalaire, comme la vitesse. La forme de vecteur de ce serait une vitesse. en.wikipedia.org/wiki/vector_(Mathematics_and_physics)


@reuscam: l'espace de tous les points des n-dimensions est isomorphe à l'espace de tous les vecteurs n-dimensions. Il pointe spécifie un vecteur de l'origine à ce point vous donnant de la longueur et de la direction. Bien sûr, beaucoup de gens veulent avoir un vecteur situé à un moment donné (c'est-à-dire un lieu de départ et une longueur et une direction). Pour cela, vous avez besoin de deux points.


5 Réponses :


3
votes

Vous avez une dépendance circulaire. Le joueur comprend Main.H, mais Main.h comprend le joueur.h. Résoudre ceci en supprimant une dépendance ou l'autre. \

joueur.h devrait inclure Health.h et CustVector.h, et à ce stade, je ne pense pas que Main.h a besoin d'inclure. Finalement, cela peut avoir besoin de joueur.h.


3 commentaires

#ifndef ... #undif est un autre moyen légèrement plus facile d'aller aussi bien


Ce n'est pas un problème si quelque chose d'autre doit utiliser Health.h? Cela ne l'inclure pas deux fois dans le programme?


Le moyen d'éviter, y compris un fichier d'en-tête plusieurs fois dans C ou C ++, il est d'utiliser un protecteur incluant. Chaque fichier d'en-tête commence par les deux lignes #ifndef actif_string et #define unique_string (remplacement de l'unique_string avec un nom que vous n'avez pas #define ailleurs ). Le fichier se termine ensuite avec #endif . en.wikipedia.org/wiki/include_guard



2
votes

inclut le travail très simple, ils ne font que commander le prétraiteur d'ajouter le contenu du fichier à l'endroit où l'inclusion est définie. L'idée de base est d'inclure des en-têtes que vous dépendez de. Dans player.h Vous devez inclure custvector.h et health.h . Dans Main Seulement, seul joueur.h , car toutes les inclres nécessaires seront transportées avec le joueur. et vous n'avez pas besoin d'inclure main.h dans joueur.h du tout.

Il est également bon de vous assurer que l'en-tête n'est inclus qu'une seule fois. Dans cette question, la solution générale est donnée Comment prévenir plusieurs définitions en C? En cas de studio visuel, vous pouvez utiliser #pragma une fois , si Borland C ++ Il y a aussi un tour mais je l'ai oublié.


2 commentaires

Je suppose que c'est ce qui me préoccupe, je ne voulais pas l'ajouter deux fois si j'ai décidé de faire quelque chose comme une classe ennemie qui avait également besoin de vecteur et de santé. Je pensais que si je mettais toutes les définitions en main.h, cela éliminerait ce problème.


@Karl Menke Main.H n'est pas un bon endroit pour le mettre, car il contiendra également des choses pour main.cpp. Vous devriez créer quelque chose comme commun.h et déplacer les références courantes là-bas. Ensuite, utilisez-le.



10
votes

La meilleure façon de penser que vos fichiers d'en-tête sont comme une "copie automatisée".

Un bon moyen d'y penser (bien que cela ne soit pas en réalité implémenté), c'est que lorsque vous compilez un fichier C ou C ++, le prétraiteur fonctionne en premier. Chaque fois qu'il rencontre une déclaration de #include, il collera réellement le contenu de ce fichier au lieu de la déclaration #include. Ceci est fait jusqu'à ce qu'il n'y ait plus d'inclure. Le tampon final est transmis au compilateur.

Ceci introduit plusieurs complexités:

Premièrement, si A.H comprend B.H et B.H inclut A.H, vous avez un problème. Parce que chaque fois que vous voulez coller un, vous auriez besoin de B et il aurait un! C'est une récursion. Pour cette raison, les fichiers d'en-tête utilisent #Ifndef, pour vous assurer que la même partie n'est pas lu plusieurs fois. Cela se passe probablement dans votre code.

Deuxièmement, votre compilateur C lit le fichier après que tous les fichiers d'en-tête ont été "aplaties", de sorte que vous devez considérer que lorsque le raisonnement de ce qui est déclaré avant quoi.


1 commentaires

C'est l'explication que j'aime plus, et celle que j'ai décrite la meilleure façon dont je comprends ce problème. C'est simple, le compilateur prend du texte et le compile. Le préprocesseur prend du texte et le prétraite. Comprend uniquement une directive de préprocesseur pour importer du texte d'un fichier externe dans le fichier actuel.



8
votes

Les autres réponses ici ont effectivement expliqué la manière dont les fichiers d'en-tête et le travail du préprocesseur. Le plus gros problème que vous avez est les dépendances circulaires, qui de l'expérience, je sais peut être une douleur royale. De plus, lorsque cela commence à se produire, le compilateur commence à se comporter de manière très étrange et à lancer des messages d'erreur qui ne sont pas très utiles. La méthode que j'ai apprise par un gourou C ++ au collège était de démarrer chaque fichier (un fichier d'en-tête par exemple) avec xxx

ceci utilise des directives de préprocesseur pour empêcher automatiquement les dépendances circulaires. Fondamentalement, j'utilise toujours une version de toutes les majuscules du nom de fichier. personnalisé-vector.h devient xxx

ceci vous permet d'inclure des fichiers willie-nillie sans créer des dépendances circulaires, car si un fichier est inclus plusieurs fois, Sa variable de préprocesseur est déjà définie, le préprocesseur saute le fichier. Il est également plus facile de travailler avec le code, car vous n'avez pas à passer à l'aide de vos anciens fichiers d'en-tête pour vous assurer que vous n'avez pas encore inclus quelque chose. Je vais répéter à nouveau, assurez-vous que les noms de variables que vous utilisez dans vos relevés #define sont uniques pour vous, sinon vous pourriez rencontrer des problèmes dans lesquels quelque chose ne s'incline pas correctement; -).

bonne chance!


6 commentaires

J'espère que cela ne vous dérange pas: j'ai édité votre exemple personnalisé_vector_cpp. *. C et *. Les fichiers CPP n'ont pas besoin d'inclure des gardes car vous ne les utilisez pas #include.


Donc, par exemple, si je devais faire une classe ennemie qui avait besoin de vecteur et de santé, et la classe de joueurs incluait déjà le vecteur et la santé, je n'aurais pas besoin de les inclure pour l'ennemi?


@John pas de problème! Bon appel, je suis habitué à la programmation des modèles où vous devez inclure les fichiers * .cpp dans les fichiers * .h. Je viens de faire l'habitude de le faire :-)


@Karl Droite, pensez au préprocesseur de créer un fichier géant avec tous les fichiers inclus. Le compilateur fonctionne ensuite sur ce grand fichier. Donc, une fois qu'un fichier a été inclus, peu importe l'endroit où vous allez, le compilateur est capable de le trouver. Cela a-t-il du sens?


Cela a du sens. Je suppose que mon seul problème est de garder une trace de l'endroit où elle est incluse. Dis par exemple, je souhaite supprimer un fichier contenant une classe qui utilise ces deux types, mais ce fichier a également la #include pour les deux types. Pour que les deux types ne soient plus inclus (mais disent que j'ai d'autres classes qui en ont encore besoin). Serait-il mieux alors d'avoir la #include pour les deux types de chaque fichier qui les utilise si cela se produit? Si le préprocesseur va les attraper, cela ne semble pas que cela importerait.


@Karl, je suis enclin à dire juste l'inclure partout, il est nécessaire et utilisez le #Ifndef ... #define Les blocs pour empêcher la circulaire. De cette façon, il n'y a pas de potentiel pour qu'il soit coupé plus tard. De même de cette façon, votre code est semi-autonome plutôt que dépendant d'un autre code non liée. Cela pourrait être une bonne question de «meilleures pratiques» pour le cas. Quoi qu'il en soit, tant que vous utilisez correctement les blocs #define, vous n'aurez aucun problème si vous incluez un fichier trop de fois.



1
votes

Vous voulez organiser votre #includes (et des bibliothèques, c'est-à-dire) dans un DAG (Dirigé, graphique acyclique). C'est la manière compliquée de dire "Éviter les cycles entre les fichiers d'en-tête":

si b comprend A, A ne devrait pas inclure b.

donc, en utilisant "un gros maître main.h "n'est pas la bonne approche, car il est difficile de #inclure que des dépendances directes seulement.

Chaque fichier .CPP doit inclure son propre fichier. Ce fichier ne devrait inclure que des choses qu'elle nécessite lui-même de compiler.

Il n'y a généralement pas Main.H , car main.cpp personne n'a besoin La définition de la principale.

En outre, vous voudrez Inclure les gardes Pour vous protéger contre plusieurs inclut.

par exemple xxx

- xxx xxx

La seule fois que vous souhaitez agréger un groupe de #include S dans une seule en-tête est lorsque vous leur fournissez une commodité pour un très présent Grande bibliothèque.

Dans votre exemple actuel, vous allez un peu trop à la mer - chaque classe n'a pas besoin de son propre fichier d'en-tête. Il peut tous aller dans main.cpp.

Le pré-déprocesseur C insère littéralement le fichier à partir d'un #include dans le fichier qui l'inclut (sauf si a déjà été inséré, c'est pourquoi vous avez besoin des gardes inclus). Il vous permet d'utiliser les classes définies dans ces fichiers car vous avez maintenant accès à leur définition.


0 commentaires