web-dev-qa-db-fra.com

C++ doit-il éliminer les fichiers d'en-tête?

De nombreux langages, tels que Java et C #, ne séparent pas la déclaration de la mise en oeuvre. C # a un concept de classe partielle, mais l'implémentation et la déclaration restent dans le même fichier.

Pourquoi C++ n'a-t-il pas le même modèle? Est-il plus pratique d'avoir des fichiers d'en-tête?

Je fais référence aux versions actuelles et à venir de la norme C++.

68
Sasha

Je bascule régulièrement entre C # et C++, et l'absence de fichiers d'en-tête en C # est l'une de mes plus grandes bévues. Je peux consulter un fichier d'en-tête et apprendre tout ce que j'ai besoin de savoir sur une classe - comment ses fonctions membres sont appelées, leur syntaxe d'appel, etc. - sans avoir à parcourir les pages du code qui implémente la classe.

Et oui, je connais les classes partielles et les régions, mais ce n’est pas la même chose. Les classes partielles aggravent le problème car une définition de classe est répartie sur plusieurs fichiers. En ce qui concerne les régions, elles ne semblent jamais être agrandies de la manière que je voudrais pour ce que je fais en ce moment, alors je dois passer du temps à développer ces petits plus jusqu'à ce que la vue soit correcte.

Peut-être que l’intellisense de Visual Studio fonctionnait mieux avec C++, je n’aurais pas une raison impérieuse de faire référence à des fichiers .h aussi souvent, mais même dans VS2008, l’intellisense de C++ ne peut pas toucher les fichiers C #.

22
Marc Bernier

Compatibilité en amont - Les fichiers d'en-tête ne sont pas éliminés, car cela entraînerait une rupture de la compatibilité en amont.

74
Gavin Miller

Les fichiers d'en-tête permettent une compilation indépendante. Vous n'avez pas besoin d'accéder aux fichiers d'implémentation ni même de les avoir pour compiler un fichier. Cela peut faciliter les versions distribuées.

Cela permet également aux SDK d'être un peu plus faciles. Vous pouvez fournir uniquement les en-têtes et certaines bibliothèques. Il y a bien sûr des moyens de contourner cela que d'autres langues utilisent.

33
Steve Rowe

Même Bjarne Stroustrup a appelé les fichiers d'en-tête un kludge.

Mais sans un format binaire standard comprenant les métadonnées nécessaires (telles que des fichiers de classe Java ou des fichiers .Net PE), je ne vois aucun moyen de mettre en œuvre cette fonctionnalité. Un ELF dépouillé ou un binaire a.out ne contient pas beaucoup d'informations à extraire. Et je ne pense pas que les informations soient jamais stockées dans des fichiers Windows XCOFF.

31
Max Lybbert

Dans Le design et l'évolution de C++ , Stroustrup donne une raison de plus ...

Le même fichier d'en-tête peut avoir plusieurs fichiers d'implémentation pouvant être traités simultanément par plusieurs programmeurs sans qu'il soit nécessaire de recourir à un système de contrôle de la source.

Cela peut paraître étrange ces jours-ci, mais je suppose que c’était un problème important lors de l’invention du C++.

18
Abhay

C a été conçu pour faire facilement l’écriture d’un compilateur. Il fait beaucoup de choses basées sur ce principe. Les pointeurs n'existent que pour faciliter l'écriture d'un compilateur, à l'instar des fichiers d'en-tête. La plupart des éléments reportés en C++ sont basés sur la compatibilité avec ces fonctionnalités implémentées pour faciliter l'écriture du compilateur.

C'est une bonne idée en fait. Quand C a été créé, C et Unix étaient en quelque sorte une paire. Unix sous C sous port, C sous Unix pouvait ainsi rapidement se propager d'une plate-forme à l'autre alors qu'un système d'exploitation basé sur Assembly devait être complètement réécrit pour être porté.

Le concept de la spécification d'une interface dans un fichier et de la mise en œuvre dans un autre n'est pas une mauvaise idée du tout, mais ce n'est pas ce que sont les fichiers d'en-tête C. Ils constituent simplement un moyen de limiter le nombre de passages qu'un compilateur doit effectuer via votre code source et d'autoriser une abstraction limitée du contrat entre les fichiers afin qu'ils puissent communiquer.

Ces éléments, pointeurs, fichiers d'en-tête, etc., n'offrent aucun avantage par rapport à un autre système. En mettant plus d'effort dans le compilateur, vous pouvez compiler un objet de référence aussi facilement qu'un pointeur sur le même code d'objet. C’est ce que C++ fait maintenant.

C est un super langage simple. Son ensemble de fonctionnalités était très limité et vous pouviez écrire un compilateur sans trop d'effort. Le portage est généralement trivial! Je n'essaie pas de dire que c'est un mauvais langage ou quoi que ce soit, c'est simplement que les objectifs principaux de C quand il a été créé peuvent laisser des restes dans le langage qui sont plus ou moins inutiles à présent, mais qui seront conservés pour des raisons de compatibilité.


Il semble que certaines personnes ne croient pas vraiment que C a été écrit pour porter Unix, alors voici: ( de )

La première version d'UNIX était écrite en langage assembleur, mais l'intention de Thompson était qu'elle soit écrite dans un langage de haut niveau.

Thompson a d'abord essayé d'utiliser Fortran en 1971 sur le PDP-7, mais a abandonné après le premier jour. Il a ensuite écrit un langage très simple, appelé B, qu'il a utilisé avec le PDP-7. Cela a fonctionné, mais il y avait des problèmes. Premièrement, parce que la mise en œuvre a été interprétée, cela allait toujours être lent. Deuxièmement, les notions de base de B, basées sur le langage BCPL orienté Word, ne convenaient tout simplement pas à une machine orientée octet comme la nouvelle PDP-11.

Ritchie a utilisé le PDP-11 pour ajouter des types à B, qui s'appelait pendant un moment NB pour "New B", puis il a commencé à écrire un compilateur. "Ainsi, la première phase de C était vraiment ces deux phases, successivement successives: d'abord, certains langages changent de B, ajoutant réellement la structure de type sans trop de changement dans la syntaxe; et faire le compilateur", a déclaré Ritchie.

"La deuxième phase a été plus lente", a-t-il expliqué à propos de la réécriture d'UNIX dans C. Thompson, commencée à l'été 1972, mais qui posait deux problèmes: déterminer comment exécuter les co-routines de base, c'est-à-dire comment passer du contrôle à un autre un autre; et la difficulté d’obtenir la structure de données appropriée, car la version originale de C n’avait pas de structures.

"La combinaison de ces facteurs a amené Ken à abandonner au cours de l'été", a déclaré Ritchie. "Au cours de l’année, j’ai ajouté des structures et probablement amélioré le code du compilateur - un meilleur code - et l’été prochain, c’est à ce moment-là que nous avons fait l’effort concerté et que nous avons effectivement refait le système d’exploitation complet en C.


Voici un exemple parfait de ce que je veux dire. D'après les commentaires:

Les pointeurs existent uniquement pour rendre l’écriture d’un compilateur plus facile? Non. Les pointeurs existent parce qu'ils représentent l'abstraction la plus simple possible par rapport à l'idée d'indirection. - Adam Rosenfield (il y a une heure)

Tu as raison. Afin de mettre en œuvre l'indirection, les pointeurs sont l'abstraction la plus simple possible à mettre en œuvre. Ils ne sont en aucun cas les plus simples à comprendre ou à utiliser. Les tableaux sont beaucoup plus faciles.

Le problème? Pour implémenter des tableaux aussi efficacement que des pointeurs, vous devez à peu près ajouter une énorme pile de code à votre compilateur.

Il n'y a aucune raison pour qu'ils n'aient pas conçu C sans pointeurs, mais avec un code comme celui-ci:

int i=0;
while(src[++i])
    dest[i]=src[i];

cela demandera beaucoup d'efforts (du côté des compilateurs) pour factoriser les ajouts explicites i + src et i + dest et lui faire créer le même code que celui-ci ferait:

while(*(dest++) = *(src++))
    ;

Factoriser cette variable "i" après le fait est difficile. Les nouveaux compilateurs peuvent le faire, mais à l'époque cela n'était tout simplement pas possible, et le système d'exploitation fonctionnant sur ce matériel minable nécessitait de petites optimisations comme celle-là.

Aujourd'hui, peu de systèmes ont besoin de ce type d'optimisation (je travaille sur l'une des plates-formes les plus lentes du marché - les décodeurs à câble et la plupart de nos contenus sont en Java) et, dans les rares cas où vous en auriez besoin, les nouveaux compilateurs C devrait être assez intelligent pour faire ce genre de conversion par lui-même.

15
Bill K

Si vous voulez C++ sans fichiers d'en-tête, j'ai de bonnes nouvelles pour vous.

Il existe déjà et s'appelle D ( http://www.digitalmars.com/d/index.html )

Techniquement, D semble être bien meilleur que le C++, mais il n’est tout simplement pas assez classique pour être utilisé dans de nombreuses applications pour le moment.

14
Jeroen Dirks

L'un des objectifs de C++ est d'être un sur-ensemble de C, ce qui le rend difficile à faire s'il ne peut pas prendre en charge les fichiers d'en-tête. Et, par extension, si vous souhaitez exciser des fichiers d’en-tête, vous pouvez également envisager d’exciser complètement le CPP (le pré-processeur, pas plus-plus); C # et Java ne spécifient pas de préprocesseurs de macros avec leurs normes (mais il faut noter que, dans certains cas, ils peuvent être et sont même utilisés même avec ces langages).

Comme C++ est conçu actuellement, vous avez besoin de prototypes, comme en C, pour vérifier statiquement tout code compilé faisant référence à des fonctions et des classes externes. Sans les fichiers d'en-tête, vous devrez saisir ces définitions de classe et déclarations de fonction avant de les utiliser. Pour que C++ n'utilise pas les fichiers d'en-tête, vous devez ajouter une fonctionnalité dans le langage qui prend en charge quelque chose comme le mot clé import de Java. Ce serait un ajout majeur et un changement. pour répondre à votre question de savoir si ce serait pratique: je ne pense pas - pas du tout.

8
Peter

Beaucoup de gens sont conscients des lacunes des fichiers d'en-tête et il existe des idées pour introduire un système de module plus puissant en C++. Vous voudrez peut-être jeter un oeil à Modules en C++ (Révision 5) de Daveed Vandevoorde.

8
Piotr Dobrogost

Il est utile de définir l'interface de classe dans un composant distinct du fichier d'implémentation.

Cela peut être fait avec des interfaces, mais si vous vous engagez dans cette voie, vous dites implicitement que les classes sont déficientes en termes de séparation de l'implémentation du contrat.

Modula 2 a eu la bonne idée, les modules de définition et les modules d'implémentation. http://www.modula2.org/reference/modules.php

La réponse de Java/C # est une implémentation implicite de la même chose (même si elle est orientée objet).

Les fichiers d'en-tête sont un kludge, car les fichiers d'en-tête expriment les détails de l'implémentation (tels que les variables privées).

En passant à Java et à C #, je constate que si un langage nécessite le support du développement IDE (tel que les interfaces de classe publique sont navigables dans les navigateurs de classe), il s'agit peut-être d'une déclaration indiquant que le code ne le permet pas. se tenir sur ses propres mérites comme étant particulièrement lisible.

Je trouve le mélange d'interface avec les détails de mise en œuvre assez épouvantable.

De manière cruciale, le manque de capacité à documenter la signature de classe publique dans un fichier concis, bien commenté et indépendant de la mise en œuvre, m'indique que la conception du langage est écrite pour la commodité de l'auteur, plutôt que pour la maintenance. Eh bien, je discute de Java et de C # maintenant.

2
polyglot

L’un des avantages de cette séparation est qu’il est facile de ne voir que l’interface, sans pour autant nécessitant un éditeur avancé.

2
Dimitri C.

Bien, C++ en soi ne devrait pas éliminer les fichiers d'en-tête à cause de la compatibilité ascendante. Cependant, je pense qu'ils sont une idée stupide en général. Si vous souhaitez distribuer une bibliothèque à source fermée, ces informations peuvent être extraites automatiquement. Si vous voulez comprendre comment utiliser une classe sans examiner l'implémentation, c'est à cela que servent les générateurs de documentation et ils font un travail bien meilleur.

2
dsimcha

Si vous voulez la raison pour laquelle cela n'arrivera jamais: cela casserait à peu près tous les logiciels C++ existants. Si vous consultez une partie de la documentation relative à la conception de comités C++, ils ont examiné diverses alternatives pour déterminer la quantité de code à décomposer.

Il serait beaucoup plus facile de changer l’instruction switch en une solution à moitié intelligente. Cela ne ferait que casser un peu de code. Cela n'arrivera toujours pas.

ÉDITÉ POUR NOUVELLE IDÉE:

La différence entre C++ et Java qui rend nécessaires les fichiers d'en-tête C++ est que les objets C++ ne sont pas nécessairement des pointeurs. En Java, toutes les instances de classe sont référencées par un pointeur, bien que cela ne ressemble pas à cela. C++ a des objets alloués sur le tas et la pile. Cela signifie que C++ a besoin d'un moyen de connaître la taille d'un objet et l'emplacement des données membres en mémoire.

1
David Thornley

Les fichiers d'en-tête font partie intégrante du langage. Sans les fichiers d'en-tête, toutes les bibliothèques statiques, les bibliothèques dynamiques, pratiquement toutes les bibliothèques précompilées deviennent inutiles. Les fichiers d'en-tête facilitent également la documentation de tout et permettent de consulter l'API d'une bibliothèque/fichier sans passer en revue chaque bit de code.

Ils facilitent également l'organisation de votre programme. Oui, vous devez basculer constamment de la source à l'en-tête, mais ils vous permettent également de définir des API internes et privées au sein des implémentations. Par exemple:

MySource.h:

extern int my_library_entry_point(int api_to_use, ...);

MySource.c:

int private_function_that_CANNOT_be_public();

int my_library_entry_point(int api_to_use, ...){
  // [...] Do stuff
}

int private_function_that_CANNOT_be_public() {

}

Si vous #include <MySource.h>, alors vous obtenez my_library_entry_point.

Si vous #include <MySource.c>, alors vous aussi obtenez private_function_that_CANNOT_be_public.

Vous voyez que cela pourrait être une très mauvaise chose si vous aviez une fonction pour obtenir une liste de mots de passe, ou une fonction qui implémentait votre algorithme de cryptage, ou une fonction qui exposerait les éléments internes d’un système d’exploitation, ou une fonction qui surchargeait les privilèges, etc.

0
Élektra

Aucune langue n'existe sans fichiers d'en-tête. C'est un mythe.

Regardez n'importe quelle distribution de bibliothèque propriétaire pour Java (je n'ai aucune expérience en C #, mais je m'attendrais à ce que ce soit la même chose). Ils ne vous donnent pas le fichier source complet; ils vous donnent simplement un fichier avec l'implémentation de chaque méthode masquée ({} ou {return null;} ou similaire) et tout ce qu'ils peuvent se cacher en se cachant. Vous ne pouvez pas appeler ça autre chose qu'un en-tête.

Cependant, il n'y a pas de raison technique pour laquelle un compilateur C ou C++ pourrait tout compter dans un fichier correctement marqué comme étant extern à moins que ce fichier ne soit compilé directement. Cependant, les coûts de compilation seraient énormes, car C et C++ ne sont pas rapides à analyser, ce qui est une considération très importante. Toute méthode plus complexe de fusion des en-têtes et des sources rencontrerait rapidement des problèmes techniques, tels que la nécessité pour le compilateur de connaître la disposition d'un objet.

0
coppro