Pourquoi C++ a-t-il besoin d'un fichier d'en-tête séparé?

Je n'ai jamais vraiment compris pourquoi C++ a besoin d'un fichier d'en-tête séparé avec les mêmes fonctions que dans le .fichier cpp. Cela rend la création de classes et leur remaniement très difficile, et cela ajoute des fichiers inutiles au projet. Et puis il y a le problème d'avoir à inclure les fichiers d'en-tête, mais ayant explicitement vérifier si elle a déjà été inclus.

C++ a été ratifié en 1998, alors pourquoi est-il conçu de cette façon? Quels sont les avantages d'avoir un en-tête séparée fichier?


question de suivi:

Comment le compilateur trouver le .fichier cpp avec le code, quand je est la .h fichier? Elle n'assume que le .rpc fichier a le même nom que le .h file, ou est-ce qu'il regarde réellement tous les fichiers dans l'arborescence des répertoires?

92
demandé sur Marius 2009-08-20 16:44:33

13 réponses

vous semblez vous demander comment séparer les définitions des déclarations, bien qu'il y ait d'autres utilisations pour les fichiers d'en-tête.

La réponse est que le C++ n'est pas "besoin" de cette. Si vous marquez tout en ligne (ce qui est automatique de toute façon pour les fonctions de Membre définies dans une définition de classe), alors il n'y a pas besoin de la séparation. Vous pouvez tout définir dans les fichiers d'en-tête.

Les raisons que vous pourriez veulent "151990920 de" séparer sont:

  1. pour améliorer les temps de construction.
  2. pour faire un lien avec le code sans avoir la source des définitions.
  3. pour éviter de tout marquer"en ligne".

si votre question plus générale est, " pourquoi C++ n'est-il pas identique à Java?", alors je dois demander, "pourquoi est-ce que vous écrivez C++ au lieu de Java?";- p

plus sérieusement, cependant, la raison est que le compilateur C++ on ne peut pas simplement entrer dans une autre unité de traduction et trouver comment utiliser ses symboles, de la manière que javac peut et fait. Le fichier d'en-tête est nécessaire pour déclarer au compilateur ce qu'il peut s'attendre d'être disponible au moment de la liaison.

Donc #include est une simple substitution textuelle. Si vous définissez tout dans les fichiers d'en-tête, le préprocesseur finit par créer une énorme copie et pâte de chaque fichier source dans votre projet, et l'alimentation dans le compilateur. Le fait que la norme C++ a été ratifiée en 1998 n'a rien à voir avec cela, c'est le fait que l'environnement de compilation pour C++ est si étroitement basé sur celui de C.

convertir mes commentaires pour répondre à votre question de suivi:

Comment le compilateur trouver le .fichier cpp avec le code

il ne le fait pas, du moins pas au moment où il compile le code qui a utilisé le fichier d'en-tête. Les fonctions que vous êtes il n'est même pas nécessaire que le lien ait déjà été écrit, peu importe que le compilateur sache dans quel fichier .cpp ils seront. Tout ce que le code appelant doit savoir au moment de la compilation est exprimé dans la déclaration de fonction. Au moment du lien, vous fournirez une liste de fichiers .o , ou de bibliothèques statiques ou dynamiques, et l'en-tête en vigueur est une promesse que les définitions des fonctions y seront quelque part.

85
répondu Steve Jessop 2016-01-14 00:58:36

C++ fait comme ça parce que C l'a fait comme ça, donc la vraie question est pourquoi C l'a fait comme ça? Wikipedia parle un peu à cela.

nouvelles langues compilées (telles que Java, C#) n'utilisez pas forward déclarations; les identificateurs sont reconnu automatiquement à partir de la source fichiers et lire directement à partir de dynamic bibliothèque de symboles. Cela signifie-tête les fichiers ne sont pas nécessaires.

71
répondu ongle 2009-08-20 12:55:32

certaines personnes considèrent les fichiers d'en-tête un avantage:

  • on prétend qu'il permet/renforce/permet la séparation de l'interface et de la mise en œuvre -- mais habituellement, ce n'est pas le cas. Les fichiers d'en-tête sont pleins de détails d'implémentation (par exemple les variables membres d'une classe doivent être spécifiées dans l'en-tête, même si elles ne font pas partie de l'interface publique), et les fonctions peuvent, et sont souvent, définies dans la ligne dans la classe déclaration dans l'en-tête, détruisant à nouveau cette séparation.
  • on dit parfois qu'il améliore le temps de compilation parce que chaque unité de traduction peut être traitée indépendamment. Et pourtant, C++ est probablement le langage le plus lent qui existe quand il s'agit des temps de compilation. Une partie de la raison est les nombreuses inclusions répétées du même en-tête. Un grand nombre d'en-têtes sont inclus par plusieurs unités de traduction, ce qui exige qu'ils soient analysés plusieurs fois.

finalement, le système d'en-tête est un artefact des années 70 quand C a été conçu. À l'époque, les ordinateurs avaient très peu de mémoire, et garder tout le module en mémoire n'était pas une option. Un compilateur devait commencer à lire le fichier en haut, puis procéder linéairement à travers le code source. L'en-tête mécanisme permet cela. Le compilateur n'a pas à envisager d'autres unités de traduction, il suffit de lire le code de haut en bas.

Et C++ conservé ce système pour assurer la compatibilité ascendante.

Aujourd'hui, ça n'a aucun sens. Il est inefficace, sujet aux erreurs et trop compliqué. Il y a de bien meilleures façons de séparer l'interface et la mise en œuvre, si que était le but.

cependant, l'une des propositions pour C++0x était d'ajouter un système de module approprié, permettant au code d'être compilé similaire à .NET ou Java, dans des modules plus grands, tous en une seule fois et sans en-têtes. Cette proposition Je n'ai pas fait la coupe en C++0x, mais je crois que c'est toujours dans la catégorie "nous aimerions faire ça plus tard". Peut-être dans un TR2 ou similaire.

46
répondu jalf 2009-08-20 13:36:21

pour ma compréhension (limitée - Je ne suis pas un développeur C normalement), c'est enraciné dans C. rappelez-vous que C Ne sait pas ce que sont les classes ou les espaces de noms, c'est juste un programme long. En outre, les fonctions doivent être déclarées avant leur utilisation.

par exemple, ce qui suit devrait donner une erreur de compilateur:

void SomeFunction() {
    SomeOtherFunction();
}

void SomeOtherFunction() {
    printf("What?");
}

l'erreur devrait être que" une autre fonction n'est pas déclarée " parce que vous l'appelez avant sa déclaration. Une façon de réparer c'est en déplaçant une autre fonction au-dessus de quelque fonction. Une autre approche consiste à déclarer d'abord la signature des fonctions:

void SomeOtherFunction();

void SomeFunction() {
    SomeOtherFunction();
}

void SomeOtherFunction() {
    printf("What?");
}

cela permet au compilateur de savoir: regardez quelque part dans le code, il y a une fonction appelée Somethotherfunction qui renvoie le vide et ne prend aucun paramètre. Donc, si vous encouter le code qui essaie d'appeler une autre fonction, ne paniquez pas et à la place aller la chercher.

maintenant, imaginez que vous avez quelque chose SomeOtherFunction dans deux différents .c fichiers. Ensuite, vous devez #include "SomeOther.c " dans certains.C. Maintenant, ajoutez quelques fonctions" privées " à une autre.C. Comme C ne connaît pas les fonctions privées, Cette fonction serait disponible dans certains cas.c ainsi.

C'est ici .les fichiers h apparaissent: ils spécifient toutes les fonctions (et variables) que vous voulez "exporter" à partir d'A.c fichier qui peut être consulté dans d'autres .c fichiers. De cette façon, vous obtenez quelque chose comme une portée publique/privée. Aussi, vous pouvez le donner .h fichier à d'autres personnes sans avoir à partager votre code source .les fichiers h sont compilés .fichiers lib.

donc la raison principale est vraiment pour la commodité, pour la protection du code source et d'avoir un peu de découplage entre les parties de votre application.

c'était C. C++ a introduit des Classes et des modificateurs privés/publics, alors même si vous pouvez toujours demander s'ils sont nécessaires, C++ AFAIK exige toujours la déclaration des fonctions avant de les utiliser. En outre, de nombreux développeurs C++ sont ou étaient des développeurs C et ont repris leurs concepts et leurs habitudes en C++ - pourquoi changer ce qui n'est pas cassé?

24
répondu Michael Stum 2009-08-20 13:13:08

Premier avantage: Si vous n'avez pas de fichiers d'en-tête, vous devez inclure les fichiers source dans d'autres fichiers source. Cela ferait que les fichiers inclus seraient compilés de nouveau lorsque le fichier inclus change.

Second avantage: il permet de partager les interfaces sans partager le code entre différentes unités (différents développeurs, équipes, entreprises, etc..)

11
répondu erelender 2009-08-20 12:52:27

le besoin de fichiers d'en-tête résulte des limites que le compilateur a pour connaître les informations de type pour les fonctions et / ou variables dans d'autres modules. Le programme compilé ou la bibliothèque n'inclut pas les informations de type requises par le compilateur pour se lier à des objets définis dans d'autres unités de compilation.

afin de compenser cette limitation, C et c++ permettent des déclarations et ces déclarations peuvent être incluses dans des modules qui utilisent avec l'aide du préprocesseur #include.

les langages comme Java ou C# d'un autre côté incluent les informations nécessaires pour la liaison dans la sortie du compilateur (class-file ou assembly). Par conséquent, il n'est plus nécessaire de maintenir des déclarations autonomes à inclure par les clients d'un module.

la raison pour laquelle l'information de liaison n'est pas incluse dans la sortie du compilateur est simple: elle n'est pas nécessaire à l'exécution (n'importe quel type la vérification a lieu au moment de la compilation). Il serait juste de perdre de l'espace. Rappelez-vous que c/c++ vient d'une époque où la taille d'un exécutable ou d'une bibliothèque avait une certaine importance.

5
répondu VoidPointer 2009-08-20 13:25:12

C++ a été conçu pour ajouter des fonctionnalités de langage de programmation moderne à l'infrastructure C, sans changer inutilement quoi que ce soit au sujet de C qui n'était pas spécifiquement sur le langage lui-même.

Oui, à ce stade (10 ans après la première norme C++ et 20 ans après qu'elle a commencé à se développer sérieusement dans l'utilisation), il est facile de se demander pourquoi elle n'a pas un système de module approprié. Évidemment, tout nouveau langage conçu aujourd'hui ne fonctionnerait pas comme C++. Mais ce n'est pas le point de C++.

le but de C++ est d'être évolutif, une continuation en douceur de la pratique existante, en ajoutant seulement de nouvelles capacités sans (trop souvent) casser des choses qui fonctionnent correctement pour sa communauté d'utilisateurs.

cela signifie qu'il rend certaines choses plus difficiles (en particulier pour les personnes qui commencent un nouveau projet), et certaines choses plus faciles (en particulier pour ceux qui maintiennent le code existant) que d'autres langues le feraient.

donc plutôt que s'attendre à ce que c++ se transforme en C# (ce qui serait inutile puisque nous avons déjà C#), pourquoi ne pas simplement choisir le bon outil pour le travail? Moi-même, je m'efforce d'écrire des morceaux significatifs de nouvelles fonctionnalités dans un langage moderne (j'utilise le C#), et j'ai une grande quantité de C++ existant que je garde en C++ parce qu'il n'y aurait pas de valeur réelle à tout réécrire. Ils s'intègrent très bien de toute façon, c'est en grande partie indolore.

4
répondu Daniel Earwicker 2009-08-20 13:24:04

il n'a pas besoin d'un fichier d'en-tête séparé avec les mêmes fonctions que dans main. Il faut seulement si vous développez une application utilisation de plusieurs fichiers de code et si vous utilisez une fonction qui n'a pas été déclaré.

c'est vraiment un problème de portée.

3
répondu Alex Rodrigues 2009-08-20 12:47:25

C++ a été ratifié en 1998, alors pourquoi est-il conçu de cette façon? Quels sont les avantages ayant un fichier d'en-tête?

en fait les fichiers d'en-tête deviennent très utiles lors de l'examen des programmes pour la première fois, vérifier les fichiers d'en-tête(en utilisant seulement un éditeur de texte) vous donne un aperçu de l'architecture du programme, contrairement à d'autres langues où vous devez utiliser des outils sophistiqués pour visualiser les classes et leurs fonctions de membre.

1
répondu Diaa Sami 2009-08-20 12:54:52

je pense que la vraie raison (historique) derrière les fichiers d'en-tête rendait comme plus facile pour les développeurs de compilateurs... mais alors, les fichiers d'en-tête do donnent des avantages.

Cochez ce post précédent pour plus de discussions...

1
répondu Paolo Tedesco 2017-05-23 12:26:27

Eh bien, vous pouvez parfaitement développer C++ Sans fichiers d'en-tête. En fait, certaines bibliothèques qui utilisent intensivement les modèles n'utilisent pas le paradigme des en-têtes/fichiers de code (voir boost). Mais en C / C++ vous ne pouvez pas utiliser quelque chose qui n'est pas déclaré. Un moyen pratique de s'en sortir est d'utiliser les fichiers d'en-tête. De plus, vous gagnez l'avantage de l'interface de partage sans code de partage/implémentation. Et je pense que ce n'était pas envisagé par les créateurs de C : quand vous utilisez des fichiers d'en-tête partagés, vous devez utiliser le célèbre:

#ifndef MY_HEADER_SWEET_GUARDIAN
#define MY_HEADER_SWEET_GUARDIAN

// [...]
// my header
// [...]

#endif // MY_HEADER_SWEET_GUARDIAN

ce n'est pas vraiment une caractéristique de la langue mais une façon pratique de traiter l'inclusion multiple.

donc, je pense que quand C a été créé, les problèmes avec la déclaration forward étaient sous-estimés et maintenant quand on utilise un langage de haut niveau comme C++ nous devons faire face à ce genre de choses.

un autre fardeau pour nous, pauvres utilisateurs de C++...

1
répondu neuro 2009-08-20 13:04:46

Eh bien, C++ a été ratifié en 1998, mais il avait été utilisé pendant beaucoup plus longtemps que cela, et la ratification était principalement fixer l'usage actuel plutôt que d'imposer la structure. Et puisque C++ était basé sur C, et que C a des fichiers d'en-tête, C++ les a aussi.

la raison principale des fichiers d'en-tête est d'activer la compilation séparée des fichiers, et de minimiser les dépendances.

dis que j'ai foo.cpp, et je veux utiliser le code de la barre.H / bar.fichiers cpp.

je peux #include "bar.h" foo.cpp, puis programmer et compiler foo.rpc, même si la barre.rpc n'existe pas. Le fichier d'en-tête agit comme une promesse au compilateur que les classes/fonctions dans la barre.h existera à l'exécution, et il a déjà tout ce qu'il faut savoir.

bien sûr, si les fonctions dans la barre.h n'ont pas de corps, lorsque j'essaie d'établir un lien de mon programme, il n'y aura pas de lien et je vais vous obtiendrez une erreur.

Un effet secondaire est que vous peut donner aux utilisateurs un fichier d'en-tête, sans révéler votre code source.

un autre est que si vous changez l'implémentation de votre code dans le *.fichier cpp, mais ne change pas la tête à tous, vous avez seulement besoin de compiler le *.fichier cpp au lieu de tout ce qui l'utilise. Bien sûr, si vous mettez beaucoup d'implémentation dans le fichier d'en-tête, alors cela devient moins utile.

1
répondu Mark Krenitsky 2009-08-20 13:41:23

si vous voulez que le compilateur trouve automatiquement les symboles définis dans d'autres fichiers, vous devez forcer le programmeur à placer ces fichiers dans des emplacements prédéfinis (comme la structure des paquets Java détermine la structure des dossiers du projet). Je préfère les fichiers d'en-tête. De plus, vous aurez besoin soit de sources de bibliothèques que vous utilisez, soit d'une manière uniforme pour mettre les informations nécessaires au compilateur dans des binaires.

0
répondu Tadeusz Kopec 2009-08-20 13:17:36