Je trouve que les fichiers d'en-tête sont utiles lors de la navigation dans les fichiers source C ++, car ils donnent un "résumé" de toutes les fonctions et données membres d'une classe. Pourquoi tant d'autres langages (comme Ruby, Python, Java, etc.) n'ont-ils pas une fonctionnalité comme celle-ci? Est-ce un domaine où la verbosité de C ++ est utile?
20
Réponses:
Le but initial des fichiers d'en-tête était de permettre la compilation en une seule passe et la modularité en C. En déclarant les méthodes avant leur utilisation, il ne permettait qu'une seule passe de compilation. Cet âge est révolu depuis longtemps grâce à nos ordinateurs puissants capables de faire de la compilation multi-passes sans aucun problème, et parfois même plus rapidement que les compilateurs C ++.
C ++ étant rétrocompatible avec C, il fallait conserver les fichiers d'en-tête, mais en ajoutant beaucoup au-dessus d'eux, ce qui a entraîné une conception assez problématique. Plus dans FQA .
Pour la modularité, les fichiers d'en-tête étaient nécessaires en tant que métadonnées sur le code dans les modules. Par exemple. quelles méthodes (et dans les classes C ++) sont disponibles dans quelle bibliothèque. Il était évident que le développeur écrivait ceci, car le temps de compilation était coûteux. De nos jours, ce n'est pas un problème que le compilateur génère ces métadonnées à partir du code lui-même. Les langages Java et .NET le font normalement.
Donc non. Les fichiers d'en-tête ne sont pas bons. Ils étaient quand nous devions encore avoir compilateur et éditeur de liens sur des disquettes séparées et la compilation prenait 30 minutes. De nos jours, ils ne font que gêner et sont signe de mauvaise conception.
la source
Bien qu'ils puissent vous être utiles comme forme de documentation, le système entourant les fichiers d'en-tête est extrêmement inefficace.
C a été conçu pour que chaque étape de compilation crée un seul module; chaque fichier source est compilé dans une exécution distincte du compilateur. Les fichiers d'en-tête, en revanche, sont injectés dans cette étape de compilation pour chacun des fichiers source qui les référencent.
Cela signifie que si votre fichier d'en-tête est inclus dans 300 fichiers source, il est analysé et compilé encore et encore, 300 fois pendant la construction de votre programme. La même chose avec le même résultat, encore et encore. C'est une énorme perte de temps, et c'est l'une des principales raisons pour lesquelles les programmes C et C ++ sont si longs à construire.
Toutes les langues modernes évitent intentionnellement cette absurde petite inefficacité. Au lieu de cela, généralement dans les langues compilées, les métadonnées nécessaires sont stockées dans la sortie de génération, permettant au fichier compilé d'agir comme une sorte de référence de recherche rapide décrivant ce que contient le fichier compilé. Tous les avantages d'un fichier d'en-tête, créé automatiquement sans travail supplémentaire de votre part.
Alternativement dans les langages interprétés, chaque module qui est chargé reste en mémoire. Référencer ou inclure ou nécessiter une bibliothèque lira et compilera le code source associé, qui restera résident jusqu'à la fin du programme. Si vous en avez également besoin ailleurs, aucun travail supplémentaire car il a déjà été chargé.
Dans les deux cas, vous pouvez "parcourir" les données créées par cette étape à l'aide des outils de la langue. Typiquement, l'EDI aura un navigateur de classe quelconque. Et si le langage a un REPL, il peut aussi souvent être utilisé pour générer un résumé de la documentation de tous les objets chargés.
la source
Ce que vous entendez par parcourir le fichier pour les fonctions et les membres de données n'est pas clair. Cependant, la plupart des IDE fournissent des outils pour parcourir la classe et voir les membres des données de classe.
Par exemple: Visual Studio a
Class View
etObject browser
fournit joliment les fonctionnalités demandées. Comme dans les captures d'écran suivantes.la source
Un inconvénient supplémentaire des fichiers d'en-tête est que le programme dépend de l'ordre de leur inclusion, et le programmeur doit prendre des mesures supplémentaires pour garantir l'exactitude.
Cela, couplé au système macro de C (hérité de C ++), conduit à de nombreux pièges et situations confuses.
Pour illustrer, si un en-tête définissait un symbole à l'aide de macros pour son utilisation et qu'un autre en-tête utilisait le symbole d'une autre manière, comme le nom d'une fonction, alors l'ordre d'inclusion influencerait grandement le résultat final. Il existe de nombreux exemples.
la source
J'ai toujours aimé les fichiers d'en-tête car ils fournissent une forme d'interface à l'implémentation, ainsi que des informations supplémentaires telles que les variables de classe, le tout dans un fichier facile à visualiser.
Je vois beaucoup de code C # (qui n'a pas besoin de 2 fichiers par classe) écrit en utilisant 2 fichiers par classe - un l'implémentation de classe réelle et un autre avec une interface. Cette conception est bonne pour la simulation (essentielle dans certains systèmes) et aide à définir la documentation de la classe sans avoir à utiliser l'EDI pour afficher les métadonnées compilées. J'irais si loin pour dire sa bonne pratique.
Donc, C / C ++ rendant obligatoire une équivalence (en quelque sorte) d'une interface dans les fichiers d'en-tête est une bonne chose.
Je sais qu'il y a des partisans d'autres systèmes qui ne les aiment pas pour des raisons comme `` il est plus difficile de pirater le code si vous devez mettre des choses dans 2 fichiers '', mais mon attitude est que le piratage du code n'est pas une bonne pratique de toute façon , et une fois que vous aurez commencé à écrire / concevoir du code avec un peu plus de réflexion, vous définirez naturellement les en-têtes / interfaces.
la source
Je dirais en fait que les fichiers d'en-têtes ne sont pas géniaux car ils brouillent l'interface et l'implémentation. L'objectif de la programmation en général, et en particulier de la POO, est d'avoir une interface définie et de masquer les détails de l'implémentation, mais un fichier d'en-tête C ++ vous montre à la fois les méthodes, l'héritage et les membres publics (interface) ainsi que les méthodes privées et les membres privés (une partie de la mise en œuvre). Sans oublier que dans certains cas, vous finissez par insérer du code ou des constructeurs dans le fichier d'en-tête, et certaines bibliothèques incluent du code de modèle dans les en-têtes, ce qui mélange vraiment l'implémentation avec l'interface.
L'intention initiale, je crois, était de permettre au code d'utiliser d'autres bibliothèques, objets, etc. sans avoir à importer l'intégralité du contenu du script. Tout ce dont vous avez besoin est l'en-tête pour compiler et lier. Il économise du temps et des cycles de cette façon. Dans ce cas, c'est une idée décente, mais ce n'est qu'une façon de résoudre ces problèmes.
En ce qui concerne la navigation dans la structure du programme, la plupart des IDE offrent cette capacité, et il existe de nombreux outils qui lanceront les interfaces, effectueront l'analyse de code, la décompilation, etc. afin que vous puissiez voir ce qui se passe sous les couvertures.
Quant à savoir pourquoi d'autres langues n'implémentent pas la même fonctionnalité? Eh bien, parce que d'autres langues viennent d'autres personnes, et ces concepteurs / créateurs ont une vision différente de la façon dont les choses devraient fonctionner.
La meilleure réponse est de vous en tenir à ce que fait le travail que vous devez faire et de vous rendre heureux.
la source
Dans de nombreux langages de programmation, lorsqu'un programme est subdivisé en plusieurs unités de compilation, le code dans une unité ne pourra utiliser des choses définies dans une autre que si le compilateur a des moyens de savoir ce que sont ces choses. Plutôt que d'exiger qu'un compilateur traitant une unité de compilation examine le texte entier de chaque unité de compilation qui définit tout ce qui est utilisé dans l'unité actuelle, il est préférable que le compilateur reçoive, pendant le traitement de chaque unité, le texte complet de l'unité à compiler avec avec un peu d'informations de tous les autres.
En C, le programmeur est responsable de la création de deux fichiers pour chaque unité - un contenant des informations qui ne seront nécessaires que lors de la compilation de cette unité, et une contenant des informations qui seront également nécessaires lors de la compilation d'autres unités. Il s'agit d'une conception plutôt méchante et hacky, mais elle évite d'avoir à définir une norme de langage spécifiant quoi que ce soit sur la façon dont les compilateurs doivent générer et traiter les fichiers intermédiaires. De nombreuses autres langues utilisent une approche différente, dans laquelle un compilateur générera à partir de chaque fichier source un fichier intermédiaire décrivant tout dans ce fichier source qui est censé être accessible au code extérieur. Cette approche évite d'avoir à dupliquer les informations dans deux fichiers source, mais elle nécessite qu'une plateforme de compilation ait défini la sémantique des fichiers d'une manière qui n'est pas requise pour C.
la source