¿Son realmente buenos los archivos de encabezado? [cerrado]


20

Encuentro que los archivos de encabezado son útiles al explorar archivos fuente de C ++, porque dan un "resumen" de todas las funciones y miembros de datos en una clase. ¿Por qué tantos otros lenguajes (como Ruby, Python, Java, etc.) no tienen una característica como esta? ¿Es esta un área donde la verbosidad de C ++ es útil?


No estoy familiarizado con las herramientas en C y C ++, pero la mayoría de los IDE / editores de código en los que he trabajado tienen una vista de "estructura" o "esquema".
Michael K

1
Los archivos de encabezado no son buenos, ¡son necesarios! - Vea también esta respuesta en SO: stackoverflow.com/a/1319570/158483
Pete

Respuestas:


31

El propósito original de los archivos de encabezado era permitir la compilación de un solo paso y la modularidad en C. Al declarar los métodos antes de usarlos, solo permitía un solo paso de compilación. Esta era ha pasado mucho tiempo gracias a que nuestras poderosas computadoras pueden realizar compilaciones de múltiples pasos sin ningún problema, y ​​a veces incluso más rápido que los compiladores de C ++.

C ++, al ser compatible con versiones anteriores, necesitaba mantener los archivos de encabezado, pero agregó muchos más, lo que resultó en un diseño bastante problemático. Más en FQA .

Para la modularidad, se necesitaban archivos de encabezado como metadatos sobre el código en los módulos. P.ej. qué métodos (y en clases de C ++) están disponibles en qué biblioteca. Era obvio que el desarrollador escribiera esto, porque el tiempo de compilación era costoso. Hoy en día, no es un problema que el compilador genere estos metadatos a partir del código mismo. Los lenguajes Java y .NET hacen esto normalmente.

Entonces no. Los archivos de encabezado no son buenos. Fueron cuando todavía teníamos que tener el compilador y el enlazador en disquetes separados y la compilación tardó 30 minutos. Hoy en día, solo se interponen y son un signo de mal diseño.


8
Estoy de acuerdo con la mayoría de lo que escribes, pero el último párrafo simplifica un poco las cosas. Los archivos de encabezado aún tienen un propósito útil para definir interfaces públicas.
Benjamin Bannier

3
@honk A eso me refería con mi pregunta. Pero creo que los IDEs modernos (como lo sugiere ElYusubov) niegan esto.
Matt Fichman

55
Podría agregar que el comité de C ++ está trabajando en módulos que harían que C y C ++ puedan funcionar sin encabezados O con encabezados utilizados de una manera más eficiente. Eso haría que esta respuesta sea más justa.
Klaim

2
@MattFichman: Generar algún archivo de encabezado es una cosa (que la mayoría de los editores / IDE de programadores pueden hacer automáticamente de alguna manera), pero el punto sobre una API pública es que efectivamente debe ser definido y diseñado por un humano real.
Benjamin Bannier

2
@honk Sí. Pero no hay ninguna razón para que esto se defina en archivos separados. Puede estar en el mismo código que la implementación. Al igual que C # lo está haciendo.
Eufórico

17

Si bien pueden ser útiles para usted como una forma de documentación, el sistema que rodea los archivos de encabezado es extraordinariamente ineficiente.

C fue diseñado para que cada paso de compilación construya un solo módulo; cada archivo fuente se compila en una ejecución separada del compilador. Los archivos de encabezado, por otro lado, se inyectan en ese paso de compilación para cada uno de los archivos de origen que hacen referencia a ellos.

Esto significa que si su archivo de encabezado se incluye en 300 archivos de origen, entonces se analiza y compila una y otra vez, 300 veces por separado mientras se construye su programa. Exactamente lo mismo con el mismo resultado, una y otra vez. Esta es una gran pérdida de tiempo, y es una de las principales razones por las que los programas C y C ++ tardan tanto en construirse.

Todos los idiomas modernos evitan intencionalmente esta pequeña ineficiencia absurda. En cambio, normalmente en los lenguajes compilados, los metadatos necesarios se almacenan en la salida de compilación, lo que permite que el archivo compilado actúe como una especie de referencia de búsqueda rápida que describe lo que contiene el archivo compilado. Todos los beneficios de un archivo de encabezado, creado automáticamente sin trabajo adicional de su parte.

Alternativamente en idiomas interpretados, cada módulo que se carga permanece en la memoria. Hacer referencia o incluir o requerir alguna biblioteca leerá y compilará el código fuente asociado, que permanece residente hasta que finaliza el programa. Si también lo necesita en otro lugar, no hay trabajo adicional ya que ya se ha cargado.

En cualquier caso, puede "examinar" los datos creados por este paso utilizando las herramientas del idioma. Por lo general, el IDE tendrá un navegador de clase de algún tipo. Y si el lenguaje tiene un REPL, a menudo también se puede usar para generar un resumen de la documentación de los objetos cargados.


55
no es estrictamente cierto: la mayoría, si no todos, los compiladores precompilan los encabezados tal como los ven, por lo que incluirlos en la siguiente unidad de compilación no es peor que encontrar el bloque de código precompilado. No es diferente a, digamos, que el código .NET repetidamente 'construye' system.data.types para cada archivo C # que compila. La razón por la que los programas C / C ++ tardan tanto es porque en realidad están compilados (y optimizados). Todo lo que necesita un programa .NET debe analizarse en IL, el tiempo de ejecución realiza la compilación real a través de JIT. Dicho esto, 300 archivos fuente en código C # también requieren bastante tiempo para compilarse.
gbjbaanb

7

No está claro qué quiere decir con buscar archivos para funciones y miembros de datos. Sin embargo, la mayoría de los IDE proporcionan herramientas para explorar la clase y ver los miembros de datos de la clase.

Por ejemplo: Visual Studio tiene Class Viewy Object browsereso proporciona muy bien la funcionalidad solicitada. Como en las siguientes capturas de pantalla.

ingrese la descripción de la imagen aquí

ingrese la descripción de la imagen aquí


4

Una desventaja adicional de los archivos de encabezado es que el programa depende del orden de su inclusión, y el programador debe tomar medidas adicionales para garantizar su corrección.

Eso, junto con el sistema macro de C (heredado por C ++), conduce a muchas trampas y situaciones confusas.

Para ilustrar, si un encabezado definió algún símbolo usando macros para su uso, y otro encabezado usó el símbolo de otra manera, como un nombre para una función, entonces el orden de inclusión influiría en gran medida en el resultado final. Hay muchos ejemplos de este tipo.


2

Siempre me gustaron los archivos de encabezado, ya que proporcionan una forma de interfaz para la implementación, junto con información adicional como variables de clase, todo en un archivo fácil de ver.

Veo un montón de código C # (que no necesita 2 archivos por clase) escrito con 2 archivos por clase, uno la implementación de clase real y otro con una interfaz. Este diseño es bueno para burlarse (esencial en algunos sistemas) y ayuda a definir la documentación de la clase sin tener que usar el IDE para ver los metadatos compilados. Iría tan lejos para decir que es una buena práctica.

Por lo tanto, C / C ++ que exige un equivalente (más o menos) de una interfaz en los archivos de encabezado es algo bueno.

Sé que hay defensores de otros sistemas que no les gustan por razones que incluyen 'es más difícil simplemente hackear el código si tienes que poner cosas en 2 archivos', pero mi actitud es que simplemente hackear el código no es una buena práctica de todos modos , y una vez que comience a escribir / diseñar código con un poco más de reflexión, definirá los encabezados / interfaces como algo natural.


Interfaz e implementación en diferentes archivos? Si necesita esto por diseño, esto sigue siendo muy común para definir la interfaz (o clase abstracta) en lenguajes como Java / C # en un archivo y ocultar las implementaciones en otros archivos. Esto no requiere archivos de encabezado viejos y complicados.
Petter Nordlander

eh? ¿No es eso lo que dije? Usted define la interfaz y la implementación de la clase en 2 archivos.
gbjbaanb

2
bueno, eso no requiere archivos de encabezado y eso no mejora los archivos de encabezado. Sí, se necesitan en idiomas con un legado, pero como concepto no se necesitan para dividir la interfaz / implementación en diferentes archivos (en realidad, lo empeoran).
Petter Nordlander

@Petter Creo que no lo entiendes, conceptualmente no son diferentes: existe un encabezado para proporcionar una interfaz en el lenguaje C, y aunque tienes razón, puedes combinar el 2 (como muchos hacen con el código C de solo encabezado) no es un Las mejores prácticas que he visto seguidas en muchos lugares. Todos los desarrolladores de C # que he visto separan la interfaz de la clase, por ejemplo. Es una buena práctica hacer esto, ya que puede incluir el archivo de interfaz en varios otros archivos sin contaminación. Ahora, si cree que esta división es una práctica recomendada (lo es), entonces la forma de lograrlo en C es mediante el uso de archivos de encabezado.
gbjbaanb

Si los encabezados fueran realmente útiles para separar la interfaz y la implementación, cosas como PImpl no serían necesarias para ocultar componentes privados y desacoplar a los usuarios finales de tener que volver a compilarlos. En cambio, obtenemos lo peor de ambos mundos. Los encabezados fingen una fachada de separación rápidamente descartada, al tiempo que abren una miríada de trampas, que requieren un código repetitivo detallado si necesita evitarlas.
underscore_d

1

De hecho, diría que los archivos de encabezados no son geniales porque enturbian la interfaz y la implementación. La intención con la programación en general, y especialmente con OOP, es tener una interfaz definida y ocultar los detalles de implementación, pero un archivo de encabezado C ++ muestra los métodos, la herencia y los miembros públicos (interfaz), así como los métodos privados y los miembros privados. (alguna parte de la implementación). Sin mencionar que en algunos casos terminas incorporando código o constructores en el archivo de encabezado, y algunas bibliotecas incluyen código de plantilla en los encabezados, que realmente combina la implementación con la interfaz.

Creo que la intención original era hacer posible que el código use otras bibliotecas, objetos, etc. sin tener que importar todo el contenido del script. Todo lo que necesitas es el encabezado para compilar y vincular. Ahorra tiempo y ciclos de esa manera. En ese caso, es una idea decente, pero es solo una forma de resolver estos problemas.

En cuanto a la exploración de la estructura del programa, la mayoría de los IDE proporcionan esa capacidad, y hay muchas herramientas que eliminarán las interfaces, realizarán análisis de código, descompilación, etc. para que pueda ver lo que sucede debajo de las cubiertas.

¿Por qué otros idiomas no implementan la misma característica? Bueno, porque otros idiomas provienen de otras personas, y esos diseñadores / creadores tienen una visión diferente de cómo deberían funcionar las cosas.

La mejor respuesta es apegarse a lo que hace el trabajo que necesita hacer y lo hace feliz.


0

En muchos lenguajes de programación, cuando un programa se subdivide en varias unidades de compilación, el código en una unidad solo podrá usar cosas definidas en otra si el compilador tiene algún medio para saber cuáles son esas cosas. En lugar de exigir que un compilador que procesa una unidad de compilación examine el texto completo de cada unidad de compilación que define cualquier cosa utilizada dentro de la unidad actual, es mejor que el compilador reciba, mientras procesa cada unidad, el texto completo de la unidad a compilar con un poco de información de todos los demás.

En C, el programador es responsable de crear dos archivos para cada unidad: uno que contenga información que solo se necesitará al compilar esa unidad y otro que contenga información que también se necesitará al compilar otras unidades. Este es un diseño bastante desagradable, pero evita la necesidad de que un estándar de lenguaje especifique algo sobre cómo los compiladores deben generar y procesar los archivos intermedios. Muchos otros lenguajes utilizan un enfoque diferente, en el que un compilador generará a partir de cada archivo fuente un archivo intermedio que describa todo en ese archivo fuente al que se supone que está accesible el código externo. Este enfoque evita la necesidad de tener información duplicada en dos archivos de origen, pero requiere que una plataforma de compilación haya definido la semántica de archivos de una manera que no es necesaria para C.

Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.