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

10 minutos de lectura

¿Que son las declaraciones directas en C
Sencillez

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

Se menciona lo siguiente:

añadir.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 qué “add“fue al compilar main.cpp. Como se mencionó anteriormente, escribir declaraciones hacia adelante para cada función que desea usar que vive en otro archivo puede volverse tedioso rápidamente.

Puedes explicar “declaración hacia adelante“más lejos? ¿Cuál es el problema si lo usamos en el main() ¿función?

  • Una “declaración directa” en realidad es solo una declaración. Vea (el final de) esta respuesta: stackoverflow.com/questions/1410563/…

    – sbi

    21 de enero de 2011 a las 10:33


1647639310 572 ¿Que son las declaraciones directas en C
scott langham

¿Por qué es necesario declarar hacia adelante en C++?

El compilador quiere asegurarse de que no haya cometido errores de ortografía o que no haya pasado la cantidad incorrecta de argumentos a la función. Por lo tanto, insiste en que primero vea una declaración de ‘agregar’ (o cualquier otro tipo, clase o función) antes de usarla.

Esto realmente solo permite que el compilador haga un mejor trabajo al validar el código y le permite ordenar los cabos sueltos para que pueda producir un archivo de objeto de apariencia ordenada. Si no tuviera que reenviar declarar cosas, el compilador produciría un archivo de objeto que tendría que contener información sobre todas las conjeturas posibles sobre cuál es la función. add puede ser. Y el enlazador tendría que contener una lógica muy inteligente para tratar de averiguar qué add en realidad tenía la intención de llamar, cuando el add la función puede vivir en un archivo de objeto diferente que el enlazador se está uniendo con el que usa agregar para producir un dll o exe. Es posible que el enlazador se equivoque add. Di que querías usar int add(int a, float b)pero accidentalmente se olvidó de escribirlo, pero el enlazador encontró un ya existente int add(int a, int b) y pensé que ese era el correcto y lo usé en su lugar. Su código se compilaría, pero no estaría haciendo lo que esperaba.

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

Diferencia entre declaración y definición.

Aparte, 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 devolución, la convención de llamada, el nombre del método, los argumentos y sus tipos. Sin embargo, no se requiere el código para el método. Para una definición, necesita la declaración y luego también el código para 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 actual .cpp o .h archivo #incluyendo el encabezado que ya contiene una declaración de la función. Sin embargo, esto puede ralentizar su compilación, especialmente si #include un encabezado en un .h en lugar de .cpp de tu programa, como todo lo que #incluye el .h que está escribiendo terminaría #incluyendo todos los encabezados para los que escribió #includes también. De repente, el compilador tiene páginas #incluidas y páginas de código que necesita compilar incluso cuando solo quería usar una o dos funciones. Para evitar esto, puede usar 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á utilizando algunas funciones, esto realmente puede hacer que sus compilaciones sean más rápidas en comparación con siempre #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.

Rompa las referencias cíclicas donde dos definiciones se usan entre sí

Además, las declaraciones anticipadas pueden ayudarlo a romper ciclos. Aquí es donde dos funciones intentan usarse entre sí. Cuando esto sucede (y es algo perfectamente válido), puede #include un archivo de encabezado, pero ese archivo de encabezado intenta #include el archivo de encabezado que está escribiendo actualmente… que luego #incluye el otro encabezado, que #incluye el que está escribiendo. Está atrapado en una situación de huevo y gallina con cada archivo de encabezado tratando de volver a #incluir al 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

Hmm… la declaración de Car se requiere aquí como Wheel tiene un puntero a Carpero Car.h no se puede incluir aquí porque daría como resultado un error del compilador. Si Car.h fue incluido, que luego trataría de incluir Wheel.h que incluiría Car.h que incluiría Wheel.h y esto continuaría para siempre, por lo que, en cambio, el compilador genera un error. La solución es reenviar declarar Car en lugar de:

class Car;     // forward declaration

class Wheel
{
    Car* car;
};

si la clase Wheel tenía métodos que necesitan llamar métodos de Caresos métodos podrían definirse en Wheel.cpp y Wheel.cpp ahora puede incluir Car.h sin causar un ciclo.

  • la declaración hacia adelante también es necesaria cuando una función es compatible con dos o más clases

    – Barún

    17/09/2014 a las 11:13

  • Hola, Scott, en cuanto a su punto sobre los tiempos de compilación: ¿diría que es una práctica común o recomendada reenviar siempre declarar e incluir encabezados según sea necesario en el archivo .cpp? Al leer su respuesta, parece que debería ser así, pero me pregunto si hay alguna advertencia.

    – Zepee

    11 de febrero de 2015 a las 19:28

  • @Zepee Es un equilibrio. Para compilaciones rápidas, diría que es una buena práctica y recomiendo probarlo. Sin embargo, puede requerir un poco de esfuerzo y líneas adicionales de código que pueden necesitar mantenimiento y actualización si los nombres de los tipos, etc. todavía se están cambiando (aunque las herramientas están mejorando para cambiar el nombre de las cosas automáticamente). Así que hay una compensación. He visto bases de código donde nadie se molesta. Si se encuentra repitiendo las mismas definiciones hacia adelante, siempre puede colocarlas en un archivo de encabezado separado e incluirlo, algo como: stackoverflow.com/questions/4300696/what-is-the-iosfwd-header

    –Scott Langham

    12 de febrero de 2015 a las 11:43

  • Se requieren declaraciones de avance cuando los archivos de encabezado se refieren entre sí: es decir, stackoverflow.com/questions/396084/…

    – Nicolás Hamilton

    17 de agosto de 2015 a las 1:00

  • Puedo ver que esto permite que los otros desarrolladores de mi equipo sean realmente malos ciudadanos del código base. Si no necesita un comentario con la declaración de reenvío, como // From Car.h entonces puedes crear algunas situaciones difíciles tratando de encontrar una definición en el camino, garantizado.

    – novios

    15 de junio de 2017 a las 19:57

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

Debido a que C++ se analiza de arriba hacia abajo, el compilador necesita conocer las cosas antes de usarlas. Entonces, cuando haces referencia a:

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 de compilación.

Entonces un ‘Declaración de reenvío‘ es justo lo que dice en la lata. Es declarar algo antes de su uso.

Por lo general, incluiría declaraciones de reenvío en un archivo de encabezado y luego incluiría ese archivo de encabezado de la misma manera que iostream está incluido.

1647639310 44 ¿Que son las declaraciones directas en C
sbi

El termino “declaración hacia adelante” en C ++ se usa principalmente solo para declaraciones de clase. Vea (el final de) esta respuesta de por qué una “declaración directa” de una clase realmente es solo un simple declaración de clase con un nombre elegante.

En otras palabras, el “forward” solo agrega lastre al término, como ninguna La declaración puede verse como directa en la medida en que declara algún identificador. antes de esta usado.

(En cuanto a lo que es un declaración a diferencia de un definiciónvea nuevamente ¿Cuál es la diferencia entre una definición y una declaración?)

Cuando el compilador ve add(3, 4) necesita saber lo que eso significa. Con la declaración directa básicamente le dices al compilador que add es una función que toma dos enteros y devuelve un entero. 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 el elemento devuelto por add.

En ese momento, el compilador no está preocupado por el real implementación de addes decir, dónde está (o si hay es incluso uno) y si compila. Eso viene a la vista más tarde, después compilar los archivos de origen cuando se invoca el enlazador.

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

¿Puede explicar la “declaración anticipada” con más detalle? ¿Cuál es el problema si lo usamos en la función main()?

es lo mismo que #include"add.h". Si lo sabe, el preprocesador expande el archivo que menciona en #includeen el archivo .cpp donde escribes el #include directiva. Eso significa que si escribes #include"add.h"obtienes lo mismo, es como si estuvieras haciendo una “declaración directa”.

asumo que add.h tiene esta linea:

int add(int x, int y); 

1647639310 579 ¿Que son las declaraciones directas en C
Jack

un apéndice rápido sobre: ​​por lo general, coloca esas referencias directas en un archivo de encabezado que pertenece al archivo .c (pp) donde se implementa la función/variable, etc. en su ejemplo se vería así: add.h:

extern int add(int a, int b);

la palabra clave extern indica que la función está realmente declarada en un archivo externo (también podría ser una biblioteca, etc.). su main.c se vería así:

#include 
#include "add.h"

int main()
{
.
.
.

  • Pero, ¿no ponemos solo las declaraciones en el archivo de cabecera? Creo que esta es la razón por la cual la función se define en “add.cpp” y, por lo tanto, ¿usa declaraciones de avance? Gracias.

    – Simplicidad

    21 de enero de 2011 a las 10:52

¿Ha sido útil esta solución?

Esta web utiliza cookies propias y de terceros para su correcto funcionamiento y para fines analíticos y para mostrarte publicidad relacionada con sus preferencias en base a un perfil elaborado a partir de tus hábitos de navegación. Al hacer clic en el botón Aceptar, acepta el uso de estas tecnologías y el procesamiento de tus datos para estos propósitos. Configurar y más información
Privacidad