lenguaje incluir headers hacer encabezados ejemplos crear como cabeceras cabecera archivos c++ header-files

incluir - ¿Debería C++ eliminar los archivos de encabezado?



headers en c (17)

Muchos idiomas, como Java, C #, no separan la declaración de la implementación. C # tiene un concepto de clase parcial, pero la implementación y la declaración aún permanecen en el mismo archivo.

¿Por qué C ++ no tiene el mismo modelo? ¿Es más práctico tener archivos de encabezado?

Me refiero a las versiones actuales y futuras del estándar C ++.


¡Oh si!

Después de codificar en Java y C #, es realmente molesto tener 2 archivos para cada clase. Así que estaba pensando cómo puedo fusionarlos sin romper el código existente.

De hecho, es realmente fácil. Simplemente ponga la definición (implementación) dentro de una sección #ifdef y agregue una definición en la línea de comando del compilador para compilar ese archivo. Eso es.

Aquí hay un ejemplo:

/* File ClassA.cpp */ #ifndef _ClassA_ #define _ClassA_ #include "ClassB.cpp" #include "InterfaceC.cpp" class ClassA : public InterfaceC { public: ClassA(void); virtual ~ClassA(void); virtual void methodC(); private: ClassB b; }; #endif #ifdef compiling_ClassA ClassA::ClassA(void) { } ClassA::~ClassA(void) { } void ClassA::methodC() { } #endif

En la línea de comando, compila ese archivo con

-D compiling_ClassA

Los otros archivos que deben incluir ClassA pueden hacer

#include "ClassA.cpp"

Por supuesto, la adición de la definición en la línea de comando se puede agregar fácilmente con una macro expansión (compilador de Visual Studio) o con una variable automática (gnu make) y usando la misma nomenclatura para el nombre de definición.


Bueno, C ++ per se no debería eliminar los archivos de encabezado debido a la compatibilidad con versiones anteriores. Sin embargo, creo que son una idea tonta en general. Si desea distribuir una lib de código cerrado, esta información puede extraerse automáticamente. Si quieres entender cómo usar una clase sin mirar la implementación, para eso están los generadores de documentación, y hacen un trabajo muchísimo mejor.


C fue hecho para hacer escribir un compilador fácilmente. Hace MUCHAS cosas basadas en ese principio. Los punteros solo existen para facilitar la escritura de un compilador, al igual que los archivos de encabezado. Muchas de las cosas transferidas a C ++ se basan en la compatibilidad con estas características implementadas para facilitar la escritura del compilador.

Es una buena idea en realidad. Cuando se creó C, C y Unix fueron una especie de pareja. C portado por Unix, Unix funcionó C. De esta forma, C y Unix podrían propagarse rápidamente de una plataforma a otra, mientras que un sistema operativo basado en el ensamblaje tuvo que reescribirse por completo para ser portado.

El concepto de especificar una interfaz en un archivo y la implementación en otro no es una mala idea, pero eso no es lo que son los archivos de encabezado C. Son simplemente una forma de limitar el número de pases que un compilador debe realizar a través de su código fuente y permiten una abstracción limitada del contrato entre los archivos para que puedan comunicarse.

Estos elementos, punteros, archivos de encabezado, etc ... realmente no ofrecen ninguna ventaja sobre otro sistema. Al poner más esfuerzo en el compilador, puede compilar un objeto de referencia tan fácilmente como un puntero al mismo código de objeto exacto. Esto es lo que C ++ hace ahora.

C es un lenguaje genial y simple. Tenía un conjunto de características muy limitado, y se podía escribir un compilador sin mucho esfuerzo. ¡Portarlo es generalmente trivial! No estoy tratando de decir que es un lenguaje malo ni nada, solo es que los objetivos principales de C cuando se creó pueden dejar remanentes en el lenguaje que ahora son más o menos innecesarios, pero se mantendrán para su compatibilidad.

Parece que algunas personas realmente no creen que C se haya escrito en el puerto de Unix, así que aquí: ( from )

La primera versión de UNIX se escribió en lenguaje ensamblador, pero la intención de Thompson era que se escribiera en un lenguaje de alto nivel.

Thompson intentó por primera vez en 1971 usar Fortran en el PDP-7, pero se dio por vencido después del primer día. Luego escribió un lenguaje muy simple al que llamó B, que comenzó en el PDP-7. Funcionó, pero hubo problemas. Primero, porque la implementación fue interpretada, siempre iba a ser lenta. En segundo lugar, las nociones básicas de B, que se basaban en el BCPL orientado a palabras, simplemente no eran adecuadas para una máquina orientada a bytes como el nuevo PDP-11.

Ritchie usó el PDP-11 para agregar tipos a B, que durante un tiempo se llamó NB para "New B", y luego comenzó a escribir un compilador para él. "Así que la primera fase de C fue en realidad estas dos fases en una breve sucesión de, en primer lugar, algunos cambios de lenguaje de B, en realidad, agregando la estructura de tipos sin demasiado cambio en la sintaxis, y haciendo el compilador", dijo Ritchie.

"La segunda fase fue más lenta", dijo sobre la reescritura de UNIX en C. Thompson comenzó en el verano de 1972, pero tenía dos problemas: averiguar cómo ejecutar las correlaciones básicas, es decir, cómo cambiar el control de un proceso a otro; y la dificultad para obtener la estructura de datos adecuada, ya que la versión original de C no tenía estructuras.

"La combinación de las cosas causó que Ken se rindiera durante el verano", dijo Ritchie. "A lo largo del año, agregué estructuras y probablemente hice que el código del compilador fuera algo mejor, mejor código, y durante el verano siguiente fue cuando hicimos el esfuerzo concertado y realmente rehicimos todo el sistema operativo en C."

Aquí hay un ejemplo perfecto de lo que quiero decir. De los comentarios:

¿Los punteros solo existen para facilitar la escritura de un compilador? No. Los indicadores existen porque son la abstracción más simple posible sobre la idea de indirección. - Adam Rosenfield (hace una hora)

Tienes razón. Para implementar la indirección, los punteros son la abstracción más simple posible de implementar. De ninguna manera son los más simples posibles de comprender o usar. Las matrices son mucho más fáciles.

¿El problema? Para implementar arreglos tan eficientemente como punteros, tienes que agregar una enorme cantidad de código a tu compilador.

No hay ninguna razón para que no hayan diseñado C sin punteros, pero con un código como este:

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

tomará mucho esfuerzo (en la parte de compiladores) factorizar las adiciones explícitas de i + src e i + dest y hacer que cree el mismo código que esto haría:

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

Factorizar esa variable "i" después del hecho es DIFÍCIL. Los nuevos compiladores pueden hacerlo, pero en aquel entonces simplemente no era posible, y el sistema operativo que se ejecutaba en ese maldito hardware necesitaba pequeñas optimizaciones como esa.

Ahora, pocos sistemas necesitan ese tipo de optimización (trabajo en una de las plataformas más lentas (decodificadores de cable, y la mayoría de nuestras cosas está en Java) y en el raro caso en que pueda necesitarla, los nuevos compiladores de C debe ser lo suficientemente inteligente como para hacer ese tipo de conversión por sí mismo.


En The Design and Evolution of C ++ , Stroustrup da una razón más ...

El mismo archivo de encabezado puede tener dos o más archivos de implementación que pueden ser trabajados simultáneamente por más de un programador sin la necesidad de un sistema de control de fuente.

Esto puede parecer extraño en estos días, pero creo que fue un problema importante cuando se inventó C ++.


Incluso Bjarne Stroustrup ha llamado kludge a los archivos de cabecera.

Pero sin un formato binario estándar que incluya los metadatos necesarios (como los archivos de clase Java o .Net PE), no veo ninguna forma de implementar la característica. Un archivo ELF eliminado o a.out no tiene mucha información que necesitaría extraer. Y no creo que la información esté almacenada en archivos Windows XCOFF.


Los archivos de encabezado permiten una compilación independiente. No necesita acceder ni tener los archivos de implementación para compilar un archivo. Esto puede hacer construcciones distribuidas más fáciles.

Esto también permite que los SDK se hagan un poco más fáciles. Puede proporcionar solo los encabezados y algunas bibliotecas. Hay, por supuesto, maneras de evitar esto que otros lenguajes usan.


Los archivos de encabezado son una parte integral del idioma. Sin archivos de encabezado, todas las bibliotecas estáticas, bibliotecas dinámicas, casi cualquier biblioteca precompilada se vuelve inútil. Los archivos de encabezado también hacen que sea más fácil documentar todo y posibilitar la revisión de la API de una biblioteca / archivo sin revisar cada bit de código.

También facilitan la organización de su programa. Sí, debe cambiar constantemente de la fuente al encabezado, pero también le permiten definir API internas y privadas dentro de las implementaciones. Por ejemplo:

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 #include <MySource.h> , entonces obtienes my_library_entry_point .

Si #include <MySource.c> , entonces también obtienes private_function_that_CANNOT_be_public .

Verás que eso podría ser algo muy malo si tuvieras una función para obtener una lista de contraseñas, o una función que implementara tu algoritmo de cifrado, o una función que expondría las funciones internas de un sistema operativo, o una función que anula los privilegios, etc.


Muchas personas son conscientes de las deficiencias de los archivos de cabecera y hay ideas para introducir un sistema de módulos más potente a C ++. Es posible que desee echar un vistazo a los módulos en C ++ (Revisión 5) por Daveed Vandevoorde.


Ningún idioma existe sin archivos de encabezado. Es un mito.

Mire cualquier distribución de biblioteca propietaria para Java (no tengo experiencia en C # para hablar, pero espero que sea la misma). No te dan el archivo fuente completo; simplemente te dan un archivo con la implementación de cada método en blanco ( {} o {return null;} o similar) y todo lo que pueden evitar escondiéndose. No se puede llamar a eso sino a un encabezado.

Sin embargo, no hay ninguna razón técnica por la que un compilador C o C ++ pueda contar todo en un archivo debidamente marcado como extern menos que ese archivo se esté compilando directamente. Sin embargo, los costos de compilación serían inmensos porque ni C ni C ++ son rápidos de analizar, y esa es una consideración muy importante. Cualquier método más complejo para fusionar encabezados y fuentes rápidamente encontraría problemas técnicos como la necesidad de que el compilador conozca el diseño de un objeto.


Normalmente paso de C # a C ++, y la falta de archivos de encabezado en C # es una de mis mayores preocupaciones. Puedo ver un archivo de cabecera y aprender todo lo que necesito saber sobre una clase: cómo se llaman sus funciones miembro, su sintaxis de llamada, etc., sin tener que pasar por las páginas del código que implementa la clase.

Y sí, sé sobre clases parciales y # regiones, pero no es lo mismo. Las clases parciales en realidad empeoran el problema, porque una definición de clase se extiende por varios archivos. En lo que respecta a las #regiones, nunca parecen estar expandidas de la manera que me gustaría para lo que estoy haciendo en este momento, así que tengo que dedicar tiempo a ampliar esas pequeñas ventajas hasta que tenga la visión correcta.

Quizás si el intellisense de Visual Studio funcionara mejor para C ++, no tendría una razón convincente para tener que referirme a archivos .h tan a menudo, pero incluso en VS2008, el intellisense de C ++ no puede tocar C # ''s


Si desea la razón por la que esto nunca sucederá, rompería prácticamente todo el software existente de C ++. Si nos fijamos en algunos de los documentos de diseño del comité de C ++, analizaron varias alternativas para ver cuánto código se rompería.

Sería mucho más fácil cambiar la declaración de cambio a algo medianamente inteligente. Eso rompería solo un pequeño código. Todavía no va a suceder.

EDITADO PARA NUEVA IDEA:

La diferencia entre C ++ y Java que hace que los archivos de encabezado C ++ sean necesarios es que los objetos C ++ no son necesariamente punteros. En Java, todas las instancias de clase se denominan puntero, aunque no se ve de esa manera. C ++ tiene objetos asignados en el montón y la pila. Esto significa que C ++ necesita una forma de saber qué tan grande será un objeto y dónde están los miembros de los datos en la memoria.


Si quieres C ++ sin archivos de encabezado, entonces tengo buenas noticias para ti.

Ya existe y se llama D ( http://www.digitalmars.com/d/index.html )

Técnicamente, D parece ser mucho mejor que C ++, pero no es lo suficientemente convencional para su uso en muchas aplicaciones en este momento.


Tiene sentido definir la interfaz de clase en un componente separado del archivo de implementación.

Se puede hacer con interfaces, pero si avanzas por ese camino, entonces estás diciendo implícitamente que las clases son deficientes en términos de separar la implementación del contrato.

Modula 2 tenía la idea correcta, los módulos de definición y los módulos de implementación. http://www.modula2.org/reference/modules.php

La respuesta de Java / C # es una implementación implícita de la misma (aunque orientada a objetos).

Los archivos de encabezado son un kludge, porque los archivos de encabezado expresan detalles de implementación (como las variables privadas).

Al pasar a Java y C #, encuentro que si un lenguaje requiere soporte IDE para el desarrollo (de modo que las interfaces de clase pública sean navegables en navegadores de clase), entonces tal vez sea una afirmación de que el código no se sostiene por sus propios méritos. siendo particularmente legible

Encuentro la mezcla de interfaz con detalle de implementación bastante horrenda.

Fundamentalmente, la falta de capacidad para documentar la firma de clase pública en un archivo conciso y bien comentado independiente de la implementación me indica que el diseño del idioma está escrito para facilitar la autoría, más bien la conveniencia del mantenimiento. Bueno, estoy hablando de Java y C # ahora.


Todavía no entiendo el punto de algunas declaraciones. La separación de la API y la implementación es algo muy bueno, pero los archivos de encabezado no son API. Hay campos privados allí. Si agrega o quita un campo privado, cambia la implementación y no la API.


Una ventaja de esta separación es que es fácil ver solo la interfaz, sin necesidad de un editor avanzado .


Uno de los objetivos de C ++ es ser un superconjunto de C, y es difícil que lo haga si no puede admitir archivos de encabezado. Y, por extensión, si desea eliminar archivos de encabezado, también puede considerar eliminar CPP (el preprocesador, no más-más) en total; tanto C # como Java no especifican los macropreprocesadores con sus estándares (pero debe tenerse en cuenta que en algunos casos pueden ser e incluso se usan incluso con estos lenguajes).

Como C ++ está diseñado en este momento, necesita prototipos, como en C, para verificar estáticamente cualquier código compilado que haga referencia a funciones y clases externas. Sin archivos de encabezado, debe escribir estas definiciones de clase y declaraciones de funciones antes de usarlas. Para que C ++ no use archivos de encabezado, deberá agregar una característica en el idioma que admita algo así como la palabra clave de import de Java. Eso sería una gran adición y cambio; para responder a su pregunta de si sería práctico: no lo creo, en absoluto.


Compatibilidad con versiones anteriores : los archivos de encabezado no se eliminan porque romperían la compatibilidad con versiones anteriores.