167 votes

Pourquoi utiliser #ifndef CLASS_H et #define CLASS_H dans un fichier .h mais pas dans un fichier .cpp?

J'ai toujours vu des gens écrire

class.h

 #ifndef CLASS_H
#define CLASS_H

//blah blah blah

#endif
 

La question est pourquoi ne font-ils pas cela également pour le fichier .cpp qui contient les définitions des fonctions de classe?

Disons que j'ai main.cpp et que main.cpp inclut class.h . class.h n’importe quoi, alors comment le fichier main.cpp peut- il savoir ce qu’il contient ?

370voto

Justin Summerlin Points 1618

Tout d'abord, pour répondre à votre première question:

Quand vous voyez cela .h fichier:

#ifndef FILE_H
#define FILE_H

/* ... Declarations etc here ... */

#endif

C'est un préprocesseur technique de la prévention d'un fichier d'en-tête d'être inclus plusieurs fois, ce qui peut être problématique pour diverses raisons. Lors de la compilation de votre projet, chaque .rpc fichier (généralement) est compilé. En termes simples, cela signifie que le compilateur va prendre votre .rpc fichier, ouvrir tous les fichiers #included , concaténer le tout dans un seul gros fichier texte, puis effectuez l'analyse syntaxique et finalement il va le convertir dans un code intermédiaire, optimiser/effectuer d'autres tâches, et enfin de générer de l'assemblée de sortie pour l'architecture cible. De ce fait, si un fichier est - #included plusieurs fois sous un .rpc fichier, le compilateur va ajouter son contenu du fichier deux fois, donc si il y a des définitions à l'intérieur du fichier, vous obtiendrez une erreur de compilation, vous indiquant que vous avez redéfini une variable. Lorsque le fichier est traité par le préprocesseur étape dans le processus de compilation, la première fois de son contenu sont atteint les deux premières lignes de vérifier si FILE_H a été défini pour le préprocesseur. Si non, il va définir FILE_H et de poursuivre le traitement du code entre elle et l' #endif directive. La prochaine fois que le contenu du fichier sont vus par le préprocesseur, la vérification FILE_H sera fausse, donc il sera immédiatement balayez vers le bas à l' #endif et se poursuivre après. Cela évite des erreurs de redéfinition.

Et pour répondre à votre deuxième préoccupation:

Dans la programmation en C++ comme une pratique générale, nous nous séparons de développement dans deux types de fichiers. On est avec une extension .h et nous appelons cela un "fichier d'en-tête." Ils fournissent habituellement une déclaration de fonctions, les classes, les structures, les variables globales, les typedefs, le prétraitement des macros et des définitions, etc. Fondamentalement, ils ont juste à vous fournir de l'information au sujet de votre code. Ensuite, nous avons la .rpc extension que nous appelons un "fichier de code." Cela permettra de fournir des définitions pour ces fonctions, les membres du groupe, tous les membres de la structure qui ont besoin de définitions, des variables globales, etc. Si l' .h fichier déclare code, et la .rpc fichier implémente cette déclaration. Pour cette raison, nous avons généralement lors de la compilation compiler chaque .rpc fichier dans un objet et de les lier ces objets (parce que vous presque jamais voir une .rpc inclure un autre fichier .rpc fichier).

Comment ces signes extérieurs sont résolus, c'est un travail de l'éditeur de liens. Lors de votre compilateur traite main.cppil reçoit les déclarations pour le code de la class.cpp en incluant la classe.h. Il a seulement besoin de savoir ce que ces fonctions ou des variables (qui est ce que une déclaration vous donne). Donc, il compile votre main.cpp fichier dans certains fichier de l'objet (l'appeler principal.obj). De même, class.cpp est compilé dans une classe.obj fichier. Pour produire l'exécutable final, un éditeur de liens est appelé à mettre en relation les fichiers de deux objets ensemble. Pour toute externe non résolu des variables ou des fonctions, le compilateur va mettre en place un stub où l'accès se passe. L'éditeur de liens prendra alors de ce talon et cherchez le code ou d'une variable dans un autre objet répertorié fichier, et si il est trouvé, il combine le code de l'objet de deux fichiers en un fichier de sortie et remplace le tampon avec de l'emplacement définitif de la fonction ou variable. De cette façon, votre code dans main.cpp peuvent appeler des fonctions et utiliser des variables dans class.cpp SI ET SEULEMENT SI ELLES SONT DÉCLARÉES DANS la classe.h.

J'espère que cela vous a été utile.

16voto

Martin B Points 14919

L' CLASS_H est un inclure de la garde; il est utilisé pour éviter que le même fichier d'en-tête inclus plusieurs fois (par des routes différentes, dans le même fichier CPP (ou, plus exactement, de la même unité de traduction), ce qui conduirait à de multiples erreurs de définition.

Inclure les gardes ne sont pas nécessaires sur les fichiers CPP parce que, par définition, le contenu du fichier CPP sont en lecture seule fois.

Vous semblez avoir interprété le comprennent gardes comme ayant la même fonction que import relevés dans d'autres langues (comme Java); ce n'est pas le cas, cependant. L' #include lui-même est à peu près équivalent à l' import dans d'autres langues.

7voto

sum1stolemyname Points 2400

Il n'a pas - au moins pendant la phase de compilation.

La traduction d'un programme c++ à partir du code source en code machine est effectuée en trois phases:

  1. Prétraitement - Le Préprocesseur traite tout le code source pour les lignes commençant par un # et exécute les directives. Dans votre cas, le contenu de votre fichier class.h est inséré à la place de la ligne, #include "class.h. Depuis que vous avez peut-être includein votre fichier d'en-tête dans plusieurs endroits, l' #ifndef clauses d'éviter les doublons de déclaration, les erreurs, depuis la directive de préprocesseur est pas défini seulement la première fois que le fichier d'en-tête est inclus.
  2. Compilation - Le Compilateur ne reste plus qu'à traduire tous les prétraitées fichiers de code source pour objet binaire des fichiers.
  3. La liaison - L'éditeur de liens de liens (d'où le nom) l'ensemble des fichiers de l'objet. Une référence à votre classe ou de l'une de ses méthodes (qui doit être déclarée dans la classe.h et définis dans class.cpp) est réglée pour que le décalage dans l'un des fichiers de l'objet. J'écris "l'un de vos fichiers de l'objet" depuis votre classe n'a pas besoin d'être défini dans un fichier nommé class.cpp, il peut être dans une bibliothèque, qui est lié à votre projet.

En résumé, les déclarations peuvent être partagés par le biais d'un fichier d'en-tête, tandis que la cartographie des déclarations de définitions est fait par l'éditeur de liens.

5voto

Brian R. Bondy Points 141769

C'est la distinction entre déclaration et définition. Les fichiers d'en-tête incluent généralement uniquement la déclaration et le fichier source contient la définition.

Pour utiliser quelque chose, il vous suffit de connaître sa déclaration et non sa définition. Seul l'éditeur de liens doit connaître la définition.

C’est pourquoi vous allez inclure un fichier d’en-tête dans un ou plusieurs fichiers sources, sans toutefois inclure un fichier source dans un autre.

Vous voulez aussi dire #include et ne pas importer.

2voto

Igor Oks Points 9737

main.cpp n'a pas besoin de savoir ce qu'il y a dans class.cpp . Il suffit de connaître les déclarations des fonctions / classes qu'il va utiliser, et ces déclarations sont dans class.h .

L'éditeur de liens relie les emplacements où les fonctions / classes déclarées dans class.h sont utilisées et leurs implémentations dans class.cpp

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X