¿Qué son las declaraciones forward en C++?


En: http://www.learncpp.com/cpp-tutorial/19-header-files /

Se menciona lo siguiente:

Add.cpp:

int add(int x, int y)
{
    return x + y;
}

Main.cpp:

#include <iostream>

int add(int x, int y); // forward declaration using function prototype

int main()
{
    using namespace std;
    cout << "The sum of 3 and 4 is " << add(3, 4) << endl;
    return 0;
}

Usamos una declaración forward para que el compilador supiera qué era "add" al compilar main.cpp. Como se mencionó anteriormente, escribir declaraciones de reenvío para cada función que desea usar que vive en otro archivo puede ser tedioso rápidamente.

Puedes explicar " adelante declaration " further? ¿Cuál es el problema si lo usamos en la función main()?

Author: Robi, 2011-01-21

8 answers

Por qué forward-declare es necesario en C++

El compilador quiere asegurarse de que no ha cometido errores ortográficos o pasado el número incorrecto de argumentos a la función. Por lo tanto, insiste en que primero ve una declaración de 'add' (o cualquier otro tipo, clase o función) antes de que se use.

Esto realmente solo permite que el compilador haga un mejor trabajo de validación del código, y le permite ordenar los cabos sueltos para que pueda producir un archivo objeto de aspecto ordenado. Si no tener que reenviar declarar cosas, el compilador produciría un archivo objeto que tendría que contener información sobre todas las conjeturas posibles en cuanto a lo que la función 'add' podría ser. Y el enlazador tendría que contener una lógica muy inteligente para tratar de averiguar qué 'add' realmente pretende llamar, cuando la función 'add' puede vivir en un archivo de objeto diferente el enlazador se une con el que utiliza add para producir un dll o exe. Es posible que el enlazador pueda obtener el add incorrecto. Dime quería usar int add(int a, float b), pero accidentalmente olvidó escribirlo, pero el enlazador encontró un int add(int a, int b) ya existente y pensó que era el correcto y lo usó en su lugar. Tu código se compilaría, pero no estaría haciendo lo que esperabas.

Por lo tanto, solo para mantener las cosas explícitas y evitar las adivinanzas, etc., el compilador insiste en declarar todo antes de que se use.

Diferencia entre declaración y definición

Como un aparte, es importante saber la diferencia entre una declaración y una definición. Una declaración simplemente da suficiente código para mostrar cómo se ve algo, por lo que para una función, este es el tipo devuelto, llamando a convención, nombre del método, argumentos y sus tipos. Pero el código para el método no es necesario. Para una definición, necesita la declaración y luego también el código para la función.

Cómo las declaraciones forward pueden reducir significativamente los tiempos de construcción

Usted puede conseguir el declaración de una función en su corriente .cpp o .archivo h # incluyendo el encabezado que ya contiene una declaración de la función. Sin embargo, esto puede ralentizar su compilación, especialmente si #incluye una cabecera en a.h en lugar de .cpp de su programa, como todo lo que #incluye .h que estás escribiendo terminaría # incluyendo todas las cabeceras que escribiste # incluye para también . De repente, el compilador #ha incluido páginas y páginas de código que necesita compilar incluso cuando solo quería utilice una o dos funciones. Para evitar esto, puede usar una declaración forward y simplemente escriba la declaración de la función usted mismo en la parte superior del archivo. Si solo estás usando unas pocas funciones, esto realmente puede hacer que tus compilaciones sean más rápidas en comparación con always #incluyendo el encabezado. Para proyectos realmente grandes, la diferencia podría ser una hora o más de tiempo de compilación reducido a unos pocos minutos.

Romper referencias cíclicas donde dos definiciones usan cada una los demás

Además, las declaraciones forward pueden ayudarle a romper los ciclos. Aquí es donde dos funciones tratan de usarse entre sí. Cuando esto sucede (y es una cosa perfectamente válida), puedes #incluir un archivo de cabecera, pero ese archivo de cabecera intenta #incluir el archivo de cabecera que estás escribiendo actualmente.... que #incluye el otro encabezado, que #incluye el que estás escribiendo. Estás atrapado en una situación de gallina y huevo con cada archivo de encabezado tratando de re #incluir el otro. Para resolver esto, puede reenviar-declarar las partes que necesita en uno de los archivos y dejar el #include fuera de ese archivo.

Eg:

Coche de Archivo.h

#include "Wheel.h"  // Include Wheel's definition so it can be used in Car.
#include <vector>

class Car
{
    std::vector<Wheel> wheels;
};

Rueda de Archivos.h

Hmm... la declaración de Coche se requiere aquí como Rueda tiene un puntero a un coche, pero el coche.h no se puede incluir aquí, ya que resultaría en un error del compilador. Si Coche.h fue incluido, que luego trataría de incluir la rueda.h que incluiría Coche.h que incluiría Rueda.h y esto continuaría para siempre, por lo que en su lugar el compilador genera un error. La solución es reenviar declarar Coche en su lugar:

class Car;     // forward declaration

class Wheel
{
    Car* car;
};

Si la rueda de clase tenía métodos que necesitan llamar a métodos de car, esos métodos podrían definirse en Rueda.cpp y Rueda.cpp ahora es capaz de incluir Coche.h sin causar un ciclo.

 309
Author: Scott Langham,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2015-04-02 05:20:10

El compilador busca que cada símbolo utilizado en la unidad de traducción actual sea declarado previamente o no en la unidad actual. Es solo una cuestión de estilo proporcionar todas las firmas de método al principio de un archivo de origen, mientras que las definiciones se proporcionan más tarde. El uso significativo es cuando se utiliza un puntero a una clase como variable miembro de otra clase.

//foo.h
class bar;    // This is useful
class foo
{
    bar* obj; // Pointer or even a reference.
};

// foo.cpp
#include "bar.h"
#include "foo.h"

Por lo tanto, use declaraciones forward en clases cuando sea posible. Si su programa solo tiene funciones (con encabezado ho archivos), luego proporcionar prototipos al principio es solo una cuestión de estilo. Este sería el caso si el archivo de encabezado estuviera presente en un programa normal con encabezado que solo tiene funciones.

 24
Author: Mahesh,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2011-01-21 10:31:00

Debido a que C++ se analiza de arriba hacia abajo, el compilador necesita saber acerca de las cosas antes de que se utilicen. Entonces, cuando usted hace referencia:

int add( int x, int y )

En la función principal, el compilador necesita saber que existe. Para probar esto intente moverlo debajo de la función principal y obtendrá un error del compilador.

Así que una ' Forward Declaration' es justo lo que dice en la lata. Está declarando algo antes de su uso.

Por lo general, incluiría declaraciones de reenvío en un archivo de cabecera y luego incluir ese archivo de cabecera de la misma manera que iostream se incluye.

 11
Author: Nick,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2011-01-21 10:16:44

El término "declaración directa" en C++ solo se usa para declaraciones de clase . Vea (el final de) esta respuesta para saber por qué una "declaración forward" de una clase realmente es solo una simple declaración de clase con un nombre elegante.

En otras palabras, el "forward" solo agrega lastre al término, ya que cualquier declaración puede verse como forward en la medida en que declara algún identificador antes de que se use.

(En cuanto a lo que es un declaración como contraposición a una definición, volver a ver a ¿Cuál es la diferencia entre una definición y una declaración?)

 9
Author: sbi,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-05-23 10:31:39

Cuando el compilador ve add(3, 4) necesita saber lo que eso significa. Con la declaración forward básicamente le dices al compilador que add es una función que toma dos ints y devuelve un int. Esta es información importante para el compilador porque necesita poner 4 y 5 en la representación correcta en la pila y necesita saber de qué tipo es la cosa devuelta por add.

En ese momento, el compilador no está preocupado por la actual implementación de add, es decir, donde está (o si hay es incluso uno) y si compila. Eso se verá más tarde, después de compilar los archivos fuente cuando se invoca el enlazador.

 1
Author: René Nyffenegger,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2011-01-21 10:18:52
int add(int x, int y); // forward declaration using function prototype

¿Puede explicar "forward declaration" más lejos? ¿Cuál es el problema si lo usamos en la función main ()?

Es lo mismo que #include"add.h". Si lo sabes,preprocesador expande el archivo que mencionas en #include, en el .archivo cpp donde se escribe la directiva #include. Eso significa que, si escribes #include"add.h", obtienes lo mismo, es como si estuvieras haciendo "forward declaration".

Asumo que add.h tiene esta línea:

int add(int x, int y); 
 1
Author: Nawaz,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2011-01-21 10:25:41

Un problema es, que el compilador no sabe, qué tipo de valor es entregado por su función; se asume, que la función devuelve un int en este caso, pero esto puede ser tan correcto como puede ser incorrecto. Otro problema es que el compilador no sabe qué tipo de argumentos espera su función, y no puede advertirle si está pasando valores del tipo incorrecto. Hay reglas especiales de "promoción", que se aplican al pasar, por ejemplo, valores de coma flotante a una función no declarada (el compilador tiene que ampliarlos para escribir double), que a menudo no es lo que la función realmente espera, lo que lleva a errores difíciles de encontrar en tiempo de ejecución.

 0
Author: Dirk,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2011-01-21 10:18:03

Una adición rápida con respecto a: por lo general se ponen esas referencias hacia adelante en un archivo de cabecera que pertenece a la .c (pp) archivo donde la función / variable etc. se aplica. en tu ejemplo se vería así: añadir.h:

extern int add(int a, int b);

La palabra clave extern indica que la función se declara realmente en un archivo externo (también podría ser una biblioteca, etc.).). tu principal.c se vería así:

#include 
#include "add.h"

int main()
{
.
.
.

 0
Author: jack,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2011-01-21 10:27:12