En C++ (y C y otros lenguajes similares) una función se dice que tiene tanto una declaración y una definición.
La declaración es simplemente una breve declaración que declara que la función existe y cuál es su interfaz. Considere una función básica add
que agrega dos enteros juntos. Su declaración podría ser similar a la siguiente:
int add(int, int);
Esto significa que "existe una función add
que toma dos números enteros y devuelve un entero". No especifica qué función tiene realmente, a pesar del hecho de que podemos hacer una buena suposición en función de su nombre.
La definición de la función es donde definimos exactamente lo que hace la función de . Esto podría ser lo que usted considera que es el código de función real. Uso de la función add
como ejemplo:
int add (int a, int b)
{
return a + b;
}
Entonces, ¿cómo encaja esto con su pregunta? Bien, supongamos que tenemos una serie de funciones matemáticas en math.cpp
:
// math.cpp
int add (int a, int b)
{
return a + b;
}
int sub(int a, int b)
{
return a - b;
}
Y también supongamos que decidimos utilizar algunos de estos en nuestra función principal en main.cpp
:
// main.cpp
#include <iostream>
int main (int argc, char* argv[])
{
std::cout << "1 + 2 = " << add(1, 2) << std::endl;
std::cout << "8 - 3 = " << sub(8, 3) << std::endl;
}
Si intenta compilar main.cpp
como está, se quejará de que no sabe qué son add
y sub
.Esto se debe a que intentas usarlos sin declarar que existen, que es exactamente para lo que es una declaración. Por lo tanto, puede hacer lo siguiente:
// main.cpp
#include <iostream>
int add(int, int);
int sub(int, int);
int main (int argc, char* argv[])
{
std::cout << "1 + 2 = " << add(1, 2) << std::endl;
std::cout << "8 - 3 = " << sub(8, 3) << std::endl;
}
Esto funcionaría, pero no es muy flexible. Si agregamos una nueva función mul
, tenemos que ir y agregar su declaración al main.cpp
y cualquier otro archivo .cpp
que lo use (lo cual es mucho trabajo si tiene muchos archivos .cpp
). Entonces, lo que hacemos es colocar todas las declaraciones en un solo archivo (por ejemplo, math.h
), por lo que solo tenemos que mantener la lista de declaraciones en un solo lugar. Luego, simplemente incluimos math.h
en cualquier archivo que use las funciones matemáticas. Este es el propósito de los archivos de cabecera (a.k.a. incluir archivos).
Esto funciona muy bien, pero podría ser incluso mejor. Tal como está, tenemos un archivo main.cpp
y un archivo math.cpp
, los cuales se compilan cada vez que compila el programa *. Si sus funciones matemáticas no cambian para nada, seguramente es mejor compilarlas una vez e insertar las definiciones precompiladas en su ejecutable siempre que recompile main.cpp
? Ese es exactamente el propósito de los archivos .lib
. Contienen el código precompilado para las definiciones de las funciones relevantes. Aún necesita el archivo de inclusión para saber qué funciones existen en la lib.
El propósito de la etapa de vinculación de la compilación es tomar estas funciones precompiladas y las funciones que acaba de compilar, y enrollarlas juntas en un solo archivo ejecutable.
Básicamente, puede ver una lib estática como el código precompilado para una serie de funciones predefinidas, y su coincidencia incluye el archivo como una herramienta para permitir que cualquier código que desee utilizar esas funciones sepa cuáles están disponibles y cuál es su descripción es.
* Esto no es estrictamente cierto, pero es suficiente para nuestros propósitos aquí.
¿Conoces la diferencia entre compilar y vincular? Esto podría ayudar a aclarar la respuesta. – sstn