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?
20
Respuestas:
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.
fuente
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.
fuente
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 View
yObject browser
eso proporciona muy bien la funcionalidad solicitada. Como en las siguientes capturas de pantalla.fuente
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.
fuente
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.
fuente
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.
fuente
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.
fuente