Nunca entendí realmente por qué C ++ necesita un archivo de encabezado separado con las mismas funciones que en el archivo .cpp. Hace que crear clases y refactorizarlas sea muy difícil, y agrega archivos innecesarios al proyecto. Y luego está el problema de tener que incluir archivos de encabezado, pero tener que verificar explícitamente si ya se ha incluido.
C ++ fue ratificado en 1998, entonces, ¿por qué está diseñado de esta manera? ¿Qué ventajas tiene tener un archivo de encabezado separado?
Siguiente pregunta:
¿Cómo encuentra el compilador el archivo .cpp con el código, cuando todo lo que incluyo es el archivo .h? ¿Asume que el archivo .cpp tiene el mismo nombre que el archivo .h, o realmente mira a través de todos los archivos en el árbol de directorios?
c++
language-design
Mario
fuente
fuente
Respuestas:
Parece que está preguntando acerca de cómo separar las definiciones de las declaraciones, aunque hay otros usos para los archivos de encabezado.
La respuesta es que C ++ no "necesita" esto. Si marca todo en línea (que de todos modos es automático para las funciones miembro definidas en una definición de clase), entonces no hay necesidad de la separación. Simplemente puede definir todo en los archivos de encabezado.
Las razones por las que puede desear separar son:
Si su pregunta más general es, "¿por qué C ++ no es idéntico a Java?", Entonces tengo que preguntar, "¿por qué escribe C ++ en lugar de Java?" ;-pags
Más en serio, sin embargo, la razón es que el compilador de C ++ no puede simplemente acceder a otra unidad de traducción y descubrir cómo usar sus símbolos, de la forma en que javac puede y lo hace. El archivo de encabezado es necesario para declarar al compilador qué puede esperar que esté disponible en el momento del enlace.
Entonces,
#include
es una sustitución textual directa. Si define todo en los archivos de encabezado, el preprocesador termina creando una enorme copia y pegando de cada archivo de origen en su proyecto, e introduciéndolo en el compilador. El hecho de que el estándar C ++ se ratificó en 1998 no tiene nada que ver con esto, es el hecho de que el entorno de compilación para C ++ se basa tan estrechamente en el de C.Convertir mis comentarios para responder a su pregunta de seguimiento:
No lo hace, al menos no en el momento en que compila el código que utilizó el archivo de encabezado. Las funciones con las que se enlaza ni siquiera necesitan haber sido escritas todavía, no importa que el compilador sepa en qué
.cpp
archivo estarán. Todo lo que el código de llamada necesita saber en el momento de la compilación se expresa en la declaración de la función. En el momento del enlace, proporcionará una lista de.o
archivos, o bibliotecas estáticas o dinámicas, y el encabezado en efecto es una promesa de que las definiciones de las funciones estarán allí en algún lugar.fuente
C ++ lo hace así porque C lo hizo así, entonces la verdadera pregunta es ¿por qué C lo hizo así? Wikipedia habla un poco de esto.
fuente
Algunas personas consideran que los archivos de encabezado son una ventaja:
En última instancia, el sistema de encabezado es un artefacto de los años 70 cuando C fue diseñado. En aquel entonces, las computadoras tenían muy poca memoria, y mantener todo el módulo en la memoria no era una opción. Un compilador tuvo que comenzar a leer el archivo en la parte superior, y luego continuar linealmente a través del código fuente. El mecanismo de encabezado permite esto. El compilador no tiene que considerar otras unidades de traducción, solo tiene que leer el código de arriba a abajo.
Y C ++ retuvo este sistema por compatibilidad con versiones anteriores.
Hoy no tiene sentido. Es ineficiente, propenso a errores y demasiado complicado. Hay formas mucho mejores de separar la interfaz y la implementación, si ese fuera el objetivo.
Sin embargo, una de las propuestas para C ++ 0x era agregar un sistema de módulos adecuado, que permitiera compilar código similar a .NET o Java, en módulos más grandes, todo de una vez y sin encabezados. Esta propuesta no hizo el corte en C ++ 0x, pero creo que todavía está en la categoría "nos encantaría hacer esto más adelante". Quizás en un TR2 o similar.
fuente
Según tengo entendido (limitado: normalmente no soy un desarrollador de C), esto se basa en C. Recuerde que C no sabe qué clases o espacios de nombres son, es solo un programa largo. Además, las funciones deben declararse antes de usarlas.
Por ejemplo, lo siguiente debería dar un error de compilación:
El error debería ser que "SomeOtherFunction no está declarado" porque lo llamas antes de su declaración. Una forma de solucionar esto es moviendo SomeOtherFunction sobre SomeFunction. Otro enfoque es declarar primero la firma de funciones:
Esto le permite al compilador saber: Busque en alguna parte del código, hay una función llamada SomeOtherFunction que devuelve void y no toma ningún parámetro. Entonces, si encuentra un código que intenta llamar a SomeOtherFunction, no entre en pánico y, en su lugar, vaya a buscarlo.
Ahora, imagine que tiene SomeFunction y SomeOtherFunction en dos archivos .c diferentes. Luego debe #incluir "SomeOther.c" en Some.c. Ahora, agregue algunas funciones "privadas" a SomeOther.c. Como C no conoce funciones privadas, esa función también estaría disponible en Some.c.
Aquí es donde entran los archivos .h: especifican todas las funciones (y variables) que desea 'Exportar' desde un archivo .c al que se puede acceder en otros archivos .c. De esa manera, obtienes algo así como un alcance público / privado. Además, puede entregar este archivo .h a otras personas sin tener que compartir su código fuente: los archivos .h también funcionan con archivos compilados .lib.
Entonces, la razón principal es realmente la conveniencia, la protección del código fuente y tener un poco de desacoplamiento entre las partes de su aplicación.
Eso fue C sin embargo. C ++ introdujo clases y modificadores privados / públicos, por lo que si bien aún se puede preguntar si son necesarios, C ++ AFAIK aún requiere la declaración de funciones antes de usarlos. Además, muchos desarrolladores de C ++ también son o fueron devleopers de C ++ y asumieron sus conceptos y hábitos a C ++. ¿Por qué cambiar lo que no está roto?
fuente
Primera ventaja: si no tiene archivos de encabezado, deberá incluir los archivos de origen en otros archivos de origen. Esto haría que los archivos incluidos se vuelvan a compilar cuando cambie el archivo incluido.
Segunda ventaja: permite compartir las interfaces sin compartir el código entre diferentes unidades (diferentes desarrolladores, equipos, empresas, etc.)
fuente
La necesidad de archivos de encabezado resulta de las limitaciones que tiene el compilador para conocer la información de tipo para funciones y / o variables en otros módulos. El programa compilado o la biblioteca no incluye la información de tipo requerida por el compilador para enlazar a cualquier objeto definido en otras unidades de compilación.
Para compensar esta limitación, C y C ++ permiten declaraciones y estas declaraciones pueden incluirse en módulos que las utilizan con la ayuda de la directiva #include del preprocesador.
Los lenguajes como Java o C #, por otro lado, incluyen la información necesaria para el enlace en la salida del compilador (archivo de clase o ensamblaje). Por lo tanto, ya no es necesario mantener declaraciones independientes para que los clientes de un módulo las incluyan.
La razón por la cual la información de enlace no se incluye en la salida del compilador es simple: no es necesaria en tiempo de ejecución (cualquier tipo de verificación ocurre en tiempo de compilación). Simplemente desperdiciaría espacio. Recuerde que C / C ++ provienen de una época en la que el tamaño de un archivo ejecutable o biblioteca importaba bastante.
fuente
C ++ fue diseñado para agregar características modernas de lenguaje de programación a la infraestructura de C, sin cambiar innecesariamente nada sobre C que no se tratara específicamente del lenguaje en sí.
Sí, en este punto (10 años después del primer estándar C ++ y 20 años después de que comenzó a crecer seriamente en uso) es fácil preguntarse por qué no tiene un sistema de módulos adecuado. Obviamente, cualquier lenguaje nuevo que se esté diseñando hoy no funcionaría como C ++. Pero ese no es el punto de C ++.
El objetivo de C ++ es ser evolutivo, una continuación fluida de la práctica existente, solo agregando nuevas capacidades sin (con demasiada frecuencia) romper cosas que funcionen adecuadamente para su comunidad de usuarios.
Esto significa que hace que algunas cosas sean más difíciles (especialmente para las personas que comienzan un nuevo proyecto), y algunas cosas más fáciles (especialmente para aquellos que mantienen el código existente) que lo harían otros idiomas.
Entonces, en lugar de esperar que C ++ se convierta en C # (lo cual sería inútil ya que ya tenemos C #), ¿por qué no elegir la herramienta adecuada para el trabajo? Yo mismo, me esfuerzo por escribir fragmentos significativos de nuevas funcionalidades en un lenguaje moderno (uso C #), y tengo una gran cantidad de C ++ existente que mantengo en C ++ porque no tendría ningún valor real reescribirlo. todas. Se integran muy bien de todos modos, por lo que es en gran medida indoloro.
fuente
Bueno, C ++ fue ratificado en 1998, pero había estado en uso durante mucho más tiempo que eso, y la ratificación estaba principalmente estableciendo el uso actual en lugar de imponer una estructura. Y dado que C ++ se basó en C, y C tiene archivos de encabezado, C ++ también los tiene.
La razón principal de los archivos de encabezado es permitir la compilación separada de archivos y minimizar las dependencias.
Digamos que tengo foo.cpp y quiero usar el código de los archivos bar.h / bar.cpp.
Puedo #incluir "bar.h" en foo.cpp, y luego programar y compilar foo.cpp incluso si bar.cpp no existe. El archivo de encabezado actúa como una promesa para el compilador de que las clases / funciones en bar.h existirán en tiempo de ejecución, y ya tiene todo lo que necesita saber.
Por supuesto, si las funciones en bar.h no tienen cuerpos cuando intento vincular mi programa, entonces no se vinculará y obtendré un error.
Un efecto secundario es que puede proporcionar a los usuarios un archivo de encabezado sin revelar su código fuente.
Otra es que si cambia la implementación de su código en el archivo * .cpp, pero no cambia el encabezado, solo necesita compilar el archivo * .cpp en lugar de todo lo que lo usa. Por supuesto, si pones mucha implementación en el archivo de encabezado, entonces esto se vuelve menos útil.
fuente
No necesita un archivo de encabezado separado con las mismas funciones que en main. Solo lo necesita si desarrolla una aplicación usando múltiples archivos de código y si usa una función que no fue declarada previamente.
Es realmente un problema de alcance.
fuente
En realidad, los archivos de encabezado se vuelven muy útiles cuando se examinan los programas por primera vez, al revisar los archivos de encabezado (usando solo un editor de texto) le da una visión general de la arquitectura del programa, a diferencia de otros lenguajes donde tiene que usar herramientas sofisticadas para ver clases y sus funciones miembro.
fuente
Creo que la verdadera (histórica) razón detrás de los archivos de cabecera como estaba haciendo más fácil para los desarrolladores de compiladores ... pero entonces, archivos de cabecera no dar ventajas.
Mira esta publicación anterior para más discusiones ...
fuente
Bueno, puedes desarrollar perfectamente C ++ sin archivos de encabezado. De hecho, algunas bibliotecas que usan plantillas de forma intensiva no usan el paradigma de los archivos de encabezado / código (ver impulso). Pero en C / C ++ no puede usar algo que no está declarado. Una forma práctica de lidiar con eso es usar archivos de encabezado. Además, obtiene la ventaja de compartir la interfaz sin compartir el código / implementación. Y creo que no fue previsto por los creadores de C: cuando usas archivos de encabezado compartidos tienes que usar el famoso:
eso no es realmente una característica del lenguaje, sino una forma práctica de lidiar con la inclusión múltiple.
Entonces, creo que cuando se creó C, se subestimaron los problemas con la declaración directa y ahora cuando se usa un lenguaje de alto nivel como C ++ tenemos que lidiar con este tipo de cosas.
Otra carga para nosotros, los usuarios pobres de C ++ ...
fuente
Si desea que el compilador descubra los símbolos definidos en otros archivos automáticamente, debe forzar al programador a colocar esos archivos en ubicaciones predefinidas (como la estructura de paquetes Java determina la estructura de carpetas del proyecto). Prefiero los archivos de encabezado. También necesitaría fuentes de bibliotecas que use o alguna forma uniforme de poner la información que necesita el compilador en binarios.
fuente