Al escribir proyectos en C ++, la gestión de segmentación de archivos es muy necesaria. Hoy, el editor de Foxin Technology Channel le traerá una explicación detallada de los archivos de encabezado y los archivos de origen en C ++. ¡Espero que sea útil para usted aprender este conocimiento!
Explicación detallada de archivos de encabezado y archivos de origen en C ++
1. Modo de compilación C ++
En general, en un programa C ++, solo hay dos tipos de archivos: archivo .cpp y archivo .h. Entre ellos, el archivo .cpp se llama archivo fuente C ++, y el código fuente de C ++ se coloca en él; mientras que el archivo .h se llama el archivo de encabezado C ++, y el código fuente de C ++ se coloca en él.
El lenguaje C ++ admite "compilación separada". En otras palabras, todo el contenido de un programa se puede dividir en diferentes partes y colocarse en diferentes archivos .cpp. Las cosas en el archivo .cpp son relativamente independientes. Al compilar (compilar), no necesita comunicarse con otros archivos. Solo necesita vincular con otros archivos de destino después de compilarlo en el archivo de destino. Por ejemplo, una función global "void a () {}" se define en el archivo A.CPP, y esta función debe llamarse en el archivo b.cpp. Aun así, los archivos A.CPP y B.CPP no necesitan conocer la existencia de los demás, pero pueden compilarlos por separado. Después de compilarlos en el archivo de destino, enlacelos y se puede ejecutar todo el programa.
¿Cómo se logra esto? Desde la perspectiva de los programas de escritura, es muy simple. En el archivo B.CPP, antes de llamar a la función "nula a ()", declare la función "nulo a ();" primero. Esto se debe a que el compilador generará una tabla de símbolos al compilar B.CPP. Los símbolos como "Void a ()" que no se pueden ver se almacenarán en esta tabla. Al vincular nuevamente, el compilador buscará la definición de este símbolo en otros archivos de objetos. Una vez encontrado, el programa se puede generar sin problemas.
Tenga en cuenta que hay dos conceptos mencionados aquí, uno es "definición" y el otro es "declaración". En pocas palabras, "definición" significa describir un símbolo de una manera completa y completa: ya sea una variable o una función, qué tipo devuelve, qué parámetros necesita, etc. "Declaración" solo declara la existencia de este símbolo, es decir, le dice al compilador que este símbolo se define en otros archivos. Lo usaré primero. Cuando vincule, vaya a otro lugar para averiguar qué es. Al definir, debe definir un símbolo (variable o función) completamente según la sintaxis de C ++, y al declarar, solo necesita escribir el prototipo de este símbolo. Cabe señalar que un símbolo se puede declarar varias veces durante todo el programa, pero debe definirse solo una vez. Imagínense, si hay dos definiciones diferentes de un símbolo, ¿a quién debería escuchar el compilador?
Este mecanismo aporta muchos beneficios a los programadores C ++, y también conduce a un método de programas de escritura. Considere que si hay una función muy comúnmente utilizada "void f () {}" que se llamará en muchos archivos .cpp en todo el programa, entonces solo necesitamos definir esta función en un archivo y declarar esta función en otros archivos. Es fácil tratar una función, y solo significa una oración para declararla. Sin embargo, ¿qué pasa si hay demasiadas funciones, como un montón de funciones matemáticas, hay cientos de ellas? ¿Puede cada programador asegurarse de escribir y escribir con precisión todas las funciones en forma de ellas?
2. ¿Qué es un archivo de encabezado?
Obviamente, la respuesta es imposible. Pero hay una manera muy sencilla de ayudar a los programadores a guardar la molestia de recordar tantos prototipos de funciones: podemos escribir todas las declaraciones de declaración de cientos de funciones primero y ponerlos en un archivo. Cuando el programador los necesita, copie todas estas cosas en su código fuente.
Este método es ciertamente factible, pero todavía es demasiado problemático y parece torpe. Entonces, el archivo de encabezado puede desempeñar su papel. El llamado archivo de encabezado en realidad tiene el mismo contenido que el contenido en el archivo .cpp, los cuales son código fuente C ++. Pero el archivo de encabezado no necesita ser compilado. Ponemos todas las declaraciones de función en un archivo de encabezado. Cuando un archivo fuente .cpp los necesita, se pueden incluir en este archivo .cpp a través de un comando macro "#include", de modo que su contenido se fusione en el archivo .cpp. Cuando se compila el archivo .cpp, se reproducen las funciones de estos archivos .h incluidos.
Demos un ejemplo. Suponga que todas las funciones matemáticas tienen solo dos: F1 y F2, luego ponemos sus definiciones en Math.cpp:
/ * math.cpp */double f1 () {// haz algo aquí ... return;} double f2 (doble a) {// hacer algo aquí ... return a * a;}/ * end de math.cpp */Y coloque la declaración de las funciones "esas" en un archivo de encabezado Math.h:
/ * math.h */double f1 (); doble f2 (doble);/ * end de Math.h */
En otro archivo main.cpp, quiero llamar a estas dos funciones, por lo que solo necesito incluir el archivo de encabezado:
/ * main.cpp */#include "math.h" main () {int number1 = f1 (); int número2 = f2 (número1);}/ * end de main.cpp */Este es un programa completo. Cabe señalar que el archivo .h no necesita escribirse después del comando del compilador, pero debe encontrarse en el lugar donde el compilador puede encontrarlo (por ejemplo, en el mismo directorio que main.cpp). main.cpp y math.cpp se pueden compilar para generar main.o y math.o respectivamente, y luego vinculan estos dos archivos de objetos, y el programa se puede ejecutar.
3. #Include
#include es un comando macro del lenguaje C que funciona antes de que el compilador se compila, es decir, cuando se precompila. El propósito de #include es incluir completamente y completamente el contenido del archivo escrito después del archivo actual. Vale la pena mencionar que no tiene otras funciones o subfunciones. Su función es reemplazar cada lugar donde aparece con el contenido del archivo que escribe detrás de él. Reemplazo de texto simple, nada más. Por lo tanto, la primera oración en el archivo Main.cpp (#include "Math.h") se reemplazará con el contenido del archivo Math.h antes de la compilación. Es decir, cuando el proceso de compilación está a punto de comenzar, el contenido de main.cpp ha cambiado:
/ * ~ main.cpp */double f1 (); double f2 (double); main () {int number1 = f1 (); int número2 = f2 (número1);}/ * final de ~ main.cpp */No más o menos, justo. Del mismo modo, si usamos funciones F1 y F2 además de Main.cpp, solo necesitamos escribir una oración #include "Math.h" antes de usar estas dos funciones.
4. ¿Qué debe escribirse en el archivo de encabezado?
A través de la discusión anterior, podemos entender que la función del archivo de encabezado se incluirá en otros .cpps. Ellos mismos no participan en la compilación, pero de hecho, sus contenidos se compilan en múltiples archivos .cpp. A través de la regla de "la definición solo puede ser una vez", podemos concluir fácilmente que solo las variables y las declaraciones de funciones deben colocarse en el archivo del encabezado, y sus definiciones no deben colocarse. Debido a que el contenido de un archivo de encabezado se introducirá en múltiples archivos .cpp diferentes, y todos se compilarán. Por supuesto, está bien poner una declaración. Si pone una definición, es equivalente a una definición de un símbolo (variable o función) que aparece en múltiples archivos. Aunque estas definiciones son las mismas, no es legal que el compilador lo haga.
Por lo tanto, una cosa que debe recordar es que en el archivo de encabezado .H, solo puede haber variables o declaraciones de funciones, y no colocan definiciones. Es decir, oraciones como: extern int a; y nulo f (); solo se puede escribir en el archivo de encabezado. Estas son las declaraciones. Si escribe una oración como int a; o nulo f () {}, luego, una vez que el archivo de encabezado se incluya en dos o más archivos .cpp, el compilador informará inmediatamente un error. (Acerca de extern, se ha discutido anteriormente, y la diferencia entre definición y declaración no se discutirá aquí). Sin embargo, hay tres excepciones a esta regla.
1. La definición del objeto const se puede escribir en el archivo de encabezado. Debido a que el objeto constante global no es declarado por EXTER por defecto, solo es válido en el archivo actual. Escriba dicho objeto en un archivo de encabezado, incluso si se incluye en varios otros archivos .cpp, el objeto solo es válido en el archivo que lo contiene y es invisible a otros archivos, por lo que no conducirá a múltiples definiciones. Al mismo tiempo, debido a que los objetos en estos archivos .cpp se incluyen desde un archivo de encabezado, esto asegura que los valores de los objetos const en estos archivos .cpp sean los mismos, lo que se puede decir que matan a dos pájaros de un solo piedra. Del mismo modo, la definición del objeto estático también se puede colocar en el archivo de encabezado.
2. La definición de funciones en línea se puede escribir en el archivo de encabezado. Debido a que la función en línea requiere que el compilador esté en línea de acuerdo con su definición, donde la encuentra, en lugar de solo una función ordinaria que se puede declarar primero y luego vincularse (las funciones en línea no estarán vinculadas), el compilador necesita ver la definición completa de la función en línea durante la compilación. Si una función en línea solo se puede definir una vez como una función normal, esto será difícil de hacer. Debido a que está bien en un archivo, puedo escribir la definición de la función en línea al principio, para que pueda ver la definición cuando la uso más tarde; Pero, ¿qué pasa si uso esta función en otros archivos? Casi no hay buena solución para esto, por lo que C ++ estipula que las funciones en línea se pueden definir varias veces en el programa. Mientras la función en línea aparezca solo una vez en un archivo .cpp, y en todos los archivos .cpp, la definición de esta función en línea es la misma, y se puede compilar. Entonces, obviamente, es muy sabio poner la definición de funciones en línea en un archivo de encabezado.
3. La definición de clase se puede escribir en el archivo de encabezado. Porque al crear un objeto de clase en un programa, el compilador solo puede saber cómo se debe establecer el objeto de esta clase cuando la definición de esta clase es completamente visible, por lo que los requisitos para la definición de una clase son básicamente los mismos que las funciones en línea. Por lo tanto, es una buena práctica poner la definición de clase en el archivo de encabezado e incluir el archivo de encabezado en el archivo .cpp utilizado en esta clase. Aquí, vale la pena mencionar que la definición de una clase contiene miembros de datos y miembros de la función. Los miembros de los datos no se definirán hasta que se cree el objeto específico (espacio asignado), pero los miembros de la función deben definirse desde el principio, que es lo que generalmente llamamos la implementación de la clase. En general, nuestro enfoque es colocar la definición de clase en el archivo de encabezado y colocar el código de implementación del miembro de la función en un archivo .cpp. Esto está bien y una buena manera. Sin embargo, hay otra forma. Es para escribir directamente el código de implementación de los miembros de la función en la definición de clase. En las clases de C ++, si los miembros de la función se definen en el cuerpo de definición de clase, el compilador considerará la función como en línea. Por lo tanto, es legal escribir la definición de miembros de la función en el cuerpo de definición de clase y armarlos en el archivo de encabezado. Tenga en cuenta que es ilegal escribir la definición del miembro de la función en el archivo de encabezado de la definición de clase y no en la definición de clase, porque el miembro de la función no está en línea en este momento. Una vez que el archivo de encabezado se incluye en dos o más archivos .cpp, este miembro de la función está redefinido.
5. Medidas de protección en archivos de encabezado
Considere que si el archivo de encabezado solo contiene declaraciones de declaración, estará bien si se incluye en el mismo archivo .cpp muchas veces, porque la aparición de declaraciones de declaración no está restringido. Sin embargo, las tres excepciones en los archivos de encabezado discutidos anteriormente también son un uso muy común de los archivos de encabezado. Luego, una vez que cualquiera de las tres excepciones anteriores aparece en un archivo de encabezado y se incluye varias veces por un .cpp, el problema será grande. Porque aunque los elementos de sintaxis en estas tres excepciones "se pueden definir en múltiples archivos de origen", "solo pueden aparecer una vez en un archivo fuente". Imagine que si AH contiene la definición de clase A y BH contiene la definición de la clase B. Dado que la definición de clase B depende de la Clase A, AH también está #incluido en BH. Ahora hay un archivo fuente que utiliza tanto la Clase A como la Clase B, por lo que el programador incluye AH y BH en este archivo fuente. En este momento, surge el problema: ¡la definición de Clase A aparece dos veces en este archivo fuente! Entonces todo el programa no se puede compilar. Se podría pensar que fue el error de un programador, debe saber que BH contiene AH, pero de hecho no debería.
Usar "#define" con compilación condicional puede resolver bien este problema. En un archivo de encabezado, un nombre se define a través de #define, y #ifndef ... #endif se compila condicionalmente para que el compilador pueda decidir si continuar compilando el contenido posterior en el encabezado en función de si el nombre se define. Aunque este método es simple, debe recordar escribirlo al escribir el archivo de encabezado.
Gracias por leer este artículo. Espero que la explicación detallada de los archivos de encabezado y los archivos de origen en C ++ introducidos en este artículo pueda ayudarlo. ¡Gracias por su apoyo de la nueva red de canales de tecnología!