Si bien puede incluir .cpp
archivos como mencionó, esta es una mala idea.
Como mencionó, las declaraciones pertenecen a los archivos de encabezado. Estos no causan problemas cuando se incluyen en varias unidades de compilación porque no incluyen implementaciones. La inclusión de la definición de una función o miembro de clase varias veces normalmente causará un problema (pero no siempre) porque el enlazador se confundirá y arrojará un error.
Lo que debería suceder es que cada .cpp
archivo incluya definiciones para un subconjunto del programa, como una clase, un grupo de funciones lógicamente organizado, variables estáticas globales (use con moderación si es que lo hace), etc.
Cada unidad de compilación ( .cpp
archivo) incluye las declaraciones que necesita para compilar las definiciones que contiene. Realiza un seguimiento de las funciones y clases a las que hace referencia, pero no contiene, por lo que el vinculador puede resolverlas más tarde cuando combina el código objeto en un archivo ejecutable o biblioteca.
Ejemplo
Foo.h
-> contiene declaración (interfaz) para la clase Foo.
Foo.cpp
-> contiene definición (implementación) para la clase Foo.
Main.cpp
-> contiene el método principal, el punto de entrada del programa. Este código crea una instancia de un Foo y lo usa.
Ambos Foo.cpp
y Main.cpp
necesitan incluir Foo.h
. Foo.cpp
lo necesita porque está definiendo el código que respalda la interfaz de la clase, por lo que necesita saber qué es esa interfaz. Main.cpp
lo necesita porque está creando un Foo e invocando su comportamiento, por lo que tiene que saber cuál es ese comportamiento, el tamaño de un Foo en la memoria y cómo encontrar sus funciones, etc., pero todavía no necesita la implementación real.
El compilador generará Foo.o
desde el Foo.cpp
cual contiene todo el código de clase Foo en forma compilada. También genera el Main.o
que incluye el método principal y referencias no resueltas a la clase Foo.
Ahora viene el enlazador, que combina los dos archivos de objetos Foo.o
y Main.o
en un archivo ejecutable. Ve las referencias de Foo sin resolver, Main.o
pero ve que Foo.o
contiene los símbolos necesarios, por lo que "conecta los puntos", por así decirlo. Una llamada de función Main.o
ahora está conectada a la ubicación real del código compilado para que, en tiempo de ejecución, el programa pueda saltar a la ubicación correcta.
Si hubiera incluido el Foo.cpp
archivo Main.cpp
, habría dos definiciones de la clase Foo. El enlazador vería esto y diría "No sé cuál elegir, así que esto es un error". El paso de compilación tendría éxito, pero la vinculación no. (A menos que simplemente no compile, Foo.cpp
pero ¿por qué está en un .cpp
archivo separado ?)
Finalmente, la idea de diferentes tipos de archivos es irrelevante para un compilador C / C ++. Compila "archivos de texto" que, con suerte, contienen un código válido para el idioma deseado. A veces puede saber el idioma en función de la extensión del archivo. Por ejemplo, compile un .c
archivo sin opciones de compilación y asumirá C, mientras que una extensión .cc
o .cpp
le indicará que asuma C ++. Sin embargo, puedo decirle fácilmente a un compilador que compile un archivo .h
o incluso .docx
como C ++, y emitirá un .o
archivo object ( ) si contiene un código C ++ válido en formato de texto sin formato. Estas extensiones son más para el beneficio del programador. Si veo Foo.h
y Foo.cpp
, inmediatamente asumo que el primero contiene la declaración de la clase y el segundo contiene la definición.