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

Resuelto Simplicity asked hace 13 años • 8 respuestas

En este enlace se mencionó lo siguiente:

agregar.cpp:

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

principal.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 directa para que el compilador supiera " add" qué era al compilar main.cpp. Como se mencionó anteriormente, escribir declaraciones directas para cada función que desee utilizar y que se encuentre en otro archivo puede resultar tedioso rápidamente.

¿ Puede explicar más la " declaración anticipada "? ¿Cuál es el problema si lo usamos en la mainfunción?

Simplicity avatar Jan 21 '11 17:01 Simplicity
Aceptado

Por qué es necesaria la declaración directa en C++

El compilador quiere asegurarse de que no haya cometido errores ortográficos ni haya pasado una cantidad incorrecta de argumentos a la función. Por lo tanto, insiste en ver primero una declaración de 'agregar' (o cualquier otro tipo, clase o función) antes de usarlo.

Esto realmente sólo permite al compilador hacer un mejor trabajo al validar el código y le permite ordenar los cabos sueltos para que pueda producir un archivo objeto de apariencia ordenada. Si no tuviera que declarar cosas, el compilador produciría un archivo objeto que tendría que contener información sobre todas las posibles conjeturas sobre cuál addpodría ser la función. Y el vinculador tendría que contener una lógica muy inteligente para intentar determinar cuál addrealmente pretendía llamar, cuando la addfunción puede residir en un archivo objeto diferente al que el vinculador se une con el que usa add para producir un dllo exe. Es posible que el vinculador se equivoque add. Digamos que querías usarlo int add(int a, float b), pero accidentalmente olvidaste escribirlo, pero el vinculador encontró uno que ya existía int add(int a, int b)y pensó que era el correcto y lo usó en su lugar. Su código se compilaría, pero no haría lo que esperaba.

Entonces, solo para mantener las cosas explícitas y evitar conjeturas, etc., el compilador insiste en que se declare todo antes de usarlo.

Diferencia entre declaración y definición

Además, es importante saber la diferencia entre una declaración y una definición. Una declaración solo proporciona suficiente código para mostrar cómo se ve algo, por lo que para una función, este es el tipo de retorno, la convención de llamada, el nombre del método, los argumentos y sus tipos. Sin embargo, no se requiere el código del método. Para una definición, necesita la declaración y luego también el código de la función.

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

Puede obtener la declaración de una función en su archivo actual .cpp#incluyendo .hel encabezado que ya contiene una declaración de la función. Sin embargo, esto puede ralentizar tu compilación, especialmente si colocas #includeun encabezado en a .hen lugar de .cppen tu programa, ya que todo lo que #incluye lo que .hestás escribiendo terminaría #incluyendo todos los encabezados para los que escribiste #includes también. De repente, el compilador ha incluido páginas y páginas de código que necesita compilar incluso cuando solo desea utilizar una o dos funciones. Para evitar esto, puede utilizar una declaración directa y simplemente escribir la declaración de la función usted mismo en la parte superior del archivo. Si solo estás usando algunas funciones, esto realmente puede hacer que tus compilaciones sean más rápidas en comparación con incluir siempre 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 se usan entre sí

Además, las declaraciones anticipadas pueden ayudarle a romper ciclos. Aquí es donde dos funciones intentan usarse entre sí. Cuando esto sucede (y es algo perfectamente válido), puede utilizar #includeun archivo de encabezado, pero ese archivo de encabezado intenta acceder #includeal archivo de encabezado que está escribiendo actualmente... que luego #incluye el otro encabezado, que #incluye el uno que estás escribiendo. Estás atrapado en una situación como la de la gallina y el huevo, con cada archivo de encabezado intentando volver a #incluir el otro. Para resolver esto, puede declarar hacia adelante las partes que necesita en uno de los archivos y dejar el #include fuera de ese archivo.

P.ej:

Archivo coche.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

CarHmm... aquí se requiere la declaración de ya que Wheeltiene un puntero a a Car, pero Car.hno se puede incluir aquí ya que resultaría en un error del compilador. Si Car.hestuviera incluido, entonces intentaría incluir Wheel.hcuál incluiría Car.hcuál incluiría Wheel.hy esto continuaría para siempre, por lo que el compilador genera un error. La solución es reenviar declarar Caren su lugar:

class Car;     // forward declaration

class Wheel
{
    Car* car;
};

Si la clase Wheeltuviera métodos que necesitaran llamar a métodos de Car, esos métodos podrían definirse Wheel.cppy Wheel.cppahora pueden incluirse Car.hsin causar un ciclo.

Scott Langham avatar Jan 21 '2011 10:01 Scott Langham

El compilador busca cada símbolo que se utiliza en la unidad de traducción actual, si está declarado previamente o no en la unidad actual. Es sólo una cuestión de estilo proporcionar todas las firmas de los métodos al principio de un archivo fuente, mientras que las definiciones se proporcionan más adelante. Su uso importante 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, utilice declaraciones directas en las clases siempre que sea posible. Si su programa solo tiene funciones (con archivos de encabezado ho), entonces proporcionar prototipos al principio es solo una cuestión de estilo. Este sería el caso de todos modos si el archivo de encabezado estuviera presente en un programa normal con un encabezado que solo tiene funciones.

Mahesh avatar Jan 21 '2011 10:01 Mahesh

Debido a que C++ se analiza de arriba hacia abajo, el compilador necesita conocer las cosas antes de usarlas. Entonces, cuando haces 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 ' Declaración Avanzada ' es justo lo que dice en la lata. Es declarar algo antes de su uso.

Generalmente incluiría declaraciones directas en un archivo de encabezado y luego incluiría ese archivo de encabezado de la misma manera que se incluye iostream .

Nick avatar Jan 21 '2011 10:01 Nick

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

En otras palabras, "forward" simplemente añade lastre al término, ya que cualquier declaración puede considerarse como forward en la medida en que declara algún identificador antes de ser utilizado.

(En cuanto a qué es una declaración en contraposición a una definición , consulte nuevamente ¿Cuál es la diferencia entre una definición y una declaración? )

sbi avatar Jan 21 '2011 10:01 sbi