tipos - ¿Qué son declaraciones directas en C++?
que es una funcion dev c++ (8)
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 directa para que el compilador supiera qué era "
add
" al compilarmain.cpp
. Como se mencionó anteriormente, escribir declaraciones avanzadas para cada función que quiera usar y que viva en otro archivo puede ser tedioso rápidamente.
¿Puedes explicar más adelante la " declaración directa "? ¿Cuál es el problema si lo usamos en la función main()
?
Cuando el compilador ve add(3, 4)
, necesita saber lo que eso significa. Con la declaración directa básicamente le dice 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 qué tipo devuelve la cosa con add.
En ese momento, al compilador no le preocupa la implementación real de add
, es decir, dónde está (o si hay una) y si compila. Esto aparece más adelante, después de compilar los archivos fuente cuando se invoca el enlazador.
Debido a que C ++ se analiza de arriba hacia abajo, el compilador debe conocer las cosas antes de que se utilicen. 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 de compilación.
Entonces, una '' Declaración Adelante '' es exactamente lo que dice en la lata. Está declarando algo antes de su uso.
En general, incluiría declaraciones anticipadas en un archivo de encabezado y luego incluiría ese archivo de encabezado de la misma manera que se incluye iostream .
El compilador busca cada símbolo que se usa en la unidad de traducción actual previamente declarada o no en la unidad actual. Es solo una cuestión de estilo al proporcionar todas las firmas de métodos al comienzo de un archivo fuente, mientras que las definiciones se proporcionan más adelante. Su uso significativo 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 forward-declarations en las clases cuando 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. 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.
El término " declaración directa " en C ++ se usa principalmente para declaraciones de clase . Vea (al final de) esta respuesta para saber por qué una "declaración directa" de una clase realmente es simplemente una declaración de clase simple con un nombre elegante.
En otras palabras, el "reenviar" simplemente agrega lastre al término, ya que cualquier declaración se puede ver como reenviada en la medida en que declara algún identificador antes de usarse.
(En cuanto a lo que es una declaración en oposición a una definición , nuevamente vea ¿Cuál es la diferencia entre una definición y una declaración? )
Un problema es que el compilador no sabe qué tipo de valor entrega su función; se supone que la función devuelve un int
en este caso, pero puede ser tan correcto como erróneo. 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 cuando se pasan, dicen valores de coma flotante a una función no declarada (el compilador debe ampliarlos para que digan doble), 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.
una adición rápida con respecto a: por lo general, coloca esas referencias avanzadas 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 establece que la función se declara realmente en un archivo externo (también podría ser una biblioteca, etc.). tu main.c se vería así:
#include #include "add.h" int main() { . . .
Por qué forward-declare es necesario en C ++
El compilador quiere asegurarse de que no haya cometido errores ortográficos o haya pasado una cantidad incorrecta de argumentos a la función. Por lo tanto, insiste en que primero ve una declaración de ''agregar'' (o cualquier otro tipo, clase o función) antes de ser utilizada.
Esto realmente solo le 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 de objeto que se vea bien. Si no tuviera que reenviar declarar cosas, el compilador produciría un archivo de objeto que debería contener información sobre todas las conjeturas posibles sobre cuál podría ser la función ''agregar''. Y el enlazador debería contener una lógica muy inteligente para tratar de determinar qué ''agregar'' realmente desea llamar, cuando la función ''agregar'' puede vivir en un archivo de objeto diferente al que el enlazador se une con el que usa agregar para producir un dll o exe Es posible que el enlazador reciba el complemento incorrecto. Digamos que quería usar int add (int a, float b), pero accidentalmente olvidó escribirlo, pero el enlazador encontró una int existente (int a, int b) y pensó que era la correcta y la 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 las conjeturas, etc., el compilador insiste en que declare 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 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 llamadas, el nombre del método, los argumentos y sus tipos. Pero el código para el método no es obligatorio. Para una definición, necesita la declaración y luego también el código para la función.
Cómo las declaraciones de avance pueden reducir significativamente los tiempos de construcción
Puede obtener la declaración de una función en su archivo .cpp o .h actual mediante #incluso el encabezado que ya contiene una declaración de la función. Sin embargo, esto puede ralentizar su compilación, especialmente si #incluye un encabezado en .h en lugar de .cpp de su programa, ya que todo lo que #incluye la .h que está escribiendo terminaría #incluyendo todos los encabezados usted 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 en la parte superior del archivo. Si solo está usando 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 comprado hasta unos minutos.
Rompe 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 ambas intentan usar el uno al otro. Cuando esto sucede (y es una cosa perfectamente válida), puede #incluir un archivo de encabezado, pero ese archivo de encabezado intenta #incluir el archivo de encabezado que está escribiendo actualmente ... que luego # incluye el otro encabezado , que # incluye el que está escribiendo. Estás atrapado en una situación de huevo y pollo con cada archivo de cabecera tratando de 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.
P.ej:
Archivo Car.h
#include "Wheel.h" // Include Wheel''s definition so it can be used in Car.
#include <vector>
class Car
{
std::vector<Wheel> wheels;
};
Archivo Wheel.h
Hmm ... aquí se requiere la declaración de Car ya que Wheel tiene un puntero a un Auto, pero Car.h no se puede incluir aquí ya que resultaría en un error del compilador. Si se incluyera Car.h, eso intentaría incluir Wheel.h, que incluiría Car.h, que incluiría Wheel.h, y esto continuaría para siempre, por lo que 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 clase Wheel tuviera métodos que necesitaran llamar a métodos de automóvil, esos métodos podrían definirse en Wheel.cpp y Wheel.cpp ahora puede incluir Car.h sin causar un ciclo.
int add(int x, int y); // forward declaration using function prototype
¿Puedes explicar "declaración adelante" más adelante? ¿Cuál es el problema si lo usamos en la función main ()?
Es lo mismo que #include"add.h"
. Si sabe, el preprocesador expande el archivo que menciona en #include
, en el archivo .cpp donde escribe la directiva #include
. Eso significa que si escribe #include"add.h"
, obtendrá lo mismo, es como si estuviera haciendo "forward declaration".
Supongo que add.h
tiene esta línea:
int add(int x, int y);