their includes headers guards functions files cpp compile and c++ header-files

c++ - includes - ¿Por qué tener archivos de encabezado y archivos.cpp?



include in c++ (9)

¿Por qué C ++ tiene archivos de encabezado y archivos .cpp?


Compilación de c ++

Una compilación en C ++ se realiza en 2 fases principales:

  1. La primera es la compilación de archivos de texto "fuente" en archivos "objeto" binarios: el archivo CPP es el archivo compilado y se compila sin ningún conocimiento sobre los otros archivos CPP (o incluso las bibliotecas), a menos que se le envíe mediante declaración sin formato Inclusión de cabecera. El archivo CPP generalmente se compila en un archivo .OBJ o .O "object".

  2. El segundo es la vinculación de todos los archivos de "objetos" y, por lo tanto, la creación del archivo binario final (ya sea una biblioteca o un ejecutable).

¿Dónde encaja el HPP en todo este proceso?

Un pobre archivo CPP solitario ...

La compilación de cada archivo CPP es independiente de todos los demás archivos CPP, lo que significa que si A.CPP necesita un símbolo definido en B.CPP, como:

// A.CPP void doSomething() { doSomethingElse(); // Defined in B.CPP } // B.CPP void doSomethingElse() { // Etc. }

No se compilará porque A.CPP no tiene forma de saber que "doSomethingElse" existe ... A menos que haya una declaración en A.CPP, como:

// A.CPP void doSomethingElse() ; // From B.CPP void doSomething() { doSomethingElse() ; // Defined in B.CPP }

Luego, si tiene C.CPP que usa el mismo símbolo, entonces copie / pegue la declaración ...

COPY / PASTA ALERTA!

Sí, hay un problema. Las copias / pastas son peligrosas y difíciles de mantener. Lo que significa que sería genial si tuviéramos alguna forma de NO copiar / pegar, y aún declarar el símbolo ... ¿Cómo podemos hacerlo? Mediante la inclusión de algún archivo de texto, que normalmente tiene el sufijo de .h, .hxx, .h ++ o, mi preferido para los archivos de C ++, .hpp:

// B.HPP (here, we decided to declare every symbol defined in B.CPP) void doSomethingElse() ; // A.CPP #include "B.HPP" void doSomething() { doSomethingElse() ; // Defined in B.CPP } // B.CPP #include "B.HPP" void doSomethingElse() { // Etc. } // C.CPP #include "B.HPP" void doSomethingAgain() { doSomethingElse() ; // Defined in B.CPP }

¿Cómo se include trabajo?

Incluir un archivo, en esencia, analizará y luego copiará y pegará su contenido en el archivo CPP.

Por ejemplo, en el siguiente código, con el encabezado A.HPP:

// A.HPP void someFunction(); void someOtherFunction();

... la fuente B.CPP:

// B.CPP #include "A.HPP" void doSomething() { // Etc. }

... se convertirá después de la inclusión:

// B.CPP void someFunction(); void someOtherFunction(); void doSomething() { // Etc. }

Una pequeña cosa: ¿por qué incluir B.HPP en B.CPP?

En el caso actual, esto no es necesario, y B.HPP tiene la declaración de la función doSomethingElse, y B.CPP tiene la definición de la función doSomethingElse (que es, en sí misma, una declaración). Pero en un caso más general, donde B.HPP se usa para declaraciones (y código en línea), no podría haber una definición correspondiente (por ejemplo, enumeraciones, estructuras simples, etc.), por lo que la inclusión podría ser necesaria si B.CPP Utiliza esas declaraciones de B.HPP. En general, es "de buen gusto" que una fuente incluya por defecto su encabezado.

Conclusión

Por lo tanto, el archivo de encabezado es necesario, ya que el compilador de C ++ no puede buscar solo las declaraciones de símbolos y, por lo tanto, debe ayudarlo al incluir esas declaraciones.

Una última palabra: debe colocar protectores de encabezado alrededor del contenido de sus archivos HPP, para asegurarse de que las inclusiones múltiples no afecten a nada, pero en general, creo que la razón principal de la existencia de archivos HPP se explica más arriba.

#ifndef B_HPP_ #define B_HPP_ // The declarations in the B.hpp file #endif // B_HPP_


A menudo, deseará tener una definición de una interfaz sin tener que enviar todo el código. Por ejemplo, si tiene una biblioteca compartida, enviaría un archivo de encabezado que define todas las funciones y símbolos utilizados en la biblioteca compartida. Sin archivos de encabezado, necesitarías enviar la fuente.

En un solo proyecto, se utilizan archivos de encabezado, IMHO, para al menos dos propósitos:

  • Claridad, es decir, al mantener las interfaces separadas de la implementación, es más fácil leer el código
  • Tiempo de compilación. Usando solo la interfaz cuando sea posible, en lugar de la implementación completa, el tiempo de compilación se puede reducir porque el compilador puede simplemente hacer una referencia a la interfaz en lugar de analizar el código real (que, en principio, solo tendría que hacerse. una sola vez).

Bueno, la razón principal sería para separar la interfaz de la implementación. El encabezado declara "qué" hará una clase (o lo que se esté implementando), mientras que el archivo cpp define "cómo" realizará esas funciones.

Esto reduce las dependencias, de modo que el código que utiliza el encabezado no necesariamente necesita conocer todos los detalles de la implementación y cualquier otra clase / encabezado necesario solo para eso. Esto reducirá los tiempos de compilación y también la cantidad de recompilación necesaria cuando algo en la implementación cambie.

No es perfecto y, por lo general, recurriría a técnicas como Pimpl Idiom para separar adecuadamente la interfaz y la implementación, pero es un buen comienzo.


Debido a que C, donde se originó el concepto, tiene 30 años, y en ese entonces, era la única forma viable de vincular el código de varios archivos.

Hoy en día, es un truco terrible que destruye totalmente el tiempo de compilación en C ++, causa innumerables dependencias innecesarias (porque las definiciones de clase en un archivo de encabezado exponen demasiada información sobre la implementación), y así sucesivamente.


Debido a que en C ++, el código ejecutable final no contiene ninguna información de símbolos, es más o menos un código de máquina puro.

Por lo tanto, necesita una manera de describir la interfaz de un fragmento de código, que es independiente del código en sí. Esta descripción está en el archivo de encabezado.


Debido a que las personas que diseñaron el formato de biblioteca no querían "desperdiciar" espacio para obtener información rara vez utilizada como macros de preprocesador C y declaraciones de funciones.

Ya que necesita esa información para decirle a su compilador "esta función está disponible más adelante cuando el enlazador está haciendo su trabajo", tuvieron que crear un segundo archivo donde se pudiera almacenar esta información compartida.

La mayoría de los idiomas después de C / C ++ almacenan esta información en la salida (por ejemplo, el código de bytes de Java) o no usan un formato precompilado, se distribuyen siempre en forma de origen y se compilan sobre la marcha (Python, Perl).


Es la forma preprocesadora de declarar interfaces. Usted coloca la interfaz (declaraciones de métodos) en el archivo de encabezado y la implementación en el cpp. Las aplicaciones que utilizan su biblioteca solo necesitan conocer la interfaz, a la que pueden acceder mediante #include.


Porque C ++ los heredó de C. Desafortunadamente.


Respondiendo a la respuesta de MadKeithV ,

Esto reduce las dependencias, de modo que el código que utiliza el encabezado no necesariamente necesita conocer todos los detalles de la implementación y cualquier otra clase / encabezado necesario solo para eso. Esto reducirá los tiempos de compilación y también la cantidad de recompilación necesaria cuando algo en la implementación cambie.

Otra razón es que un encabezado da una identificación única a cada clase.

Así que si tenemos algo como

class A {..}; class B : public A {...}; class C { include A.cpp; include B.cpp; ..... };

Tendremos errores, cuando intentemos construir el proyecto, ya que A es parte de B, con encabezados evitaríamos este tipo de dolor de cabeza ...