Que sont les déclarations forward en C++?

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

le texte suivant est mentionné:

ajouter.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;
}

nous avons utilisé une déclaration forward afin que le compilateur sache ce que " add "était en compilant main.cpp . Comme mentionné précédemment, en écrivant des déclarations forward pour chaque fonction vous souhaitez utiliser qui vit dans un autre fichier peut être fastidieux rapidement.

Pouvez-vous expliquer " déclaration anticipée "" de plus? Quel est le problème si nous l'utilisons dans le main() fonction?

158
demandé sur Robi 2011-01-21 13:13:34

8 réponses

pourquoi la déclaration à terme est nécessaire en C++

le compilateur veut s'assurer que vous n'avez pas fait de fautes d'orthographe ou passé le mauvais nombre d'arguments à la fonction. Ainsi, il insiste sur le fait qu'il voit d'abord une déclaration de "Ajouter" (ou tout autre type, classe ou Fonction) avant qu'il ne soit utilisé.

- Ce vraiment juste permet au compilateur de faire un meilleur travail de validation du code, et permet de ranger les bouts de sorte qu'il peut produire un dossier d'objet à la recherche soignée. Si vous n'aviez pas à transmettre declarate things, le compilateur produirait un fichier objet qui devrait contenir des informations sur toutes les suppositions possibles quant à ce que pourrait être la fonction "add". Et le linker devrait contenir une logique très intelligente pour essayer de trouver quel " add "vous aviez l'intention d'appeler, lorsque la fonction" add " peut vivre dans un fichier objet différent que le linker rejoint avec celui qui utilise add pour produire une dll ou exe. C'est possible que le linker peut obtenir le mauvais add. Disons que vous vouliez utiliser int add(int a, float b), mais que vous avez accidentellement oublié de l'écrire, mais le linker a trouvé un int add déjà existant(int a, int b) et a pensé que c'était le bon et l'a utilisé à la place. Votre code se compilerait, mais ne ferait pas ce à quoi vous vous attendiez.

donc, juste pour garder les choses explicites et éviter les devinettes etc, le compilateur insiste pour que vous déclariez tout avant qu'il ne soit utilisé.

différence entre la déclaration et la définition

en aparté, il est important de connaître la différence entre une déclaration et une définition. Une déclaration donne juste assez de code pour montrer à quoi ressemble quelque chose, donc pour une fonction, c'est le type de retour, appelant la convention, le nom de la méthode, les arguments et leurs types. Mais le code de la méthode n'est pas obligatoire. Pour une définition, vous avez besoin de la déclaration et ensuite aussi du code pour la fonction.

Comment faire de l'avant-déclarations peuvent réduire considérablement les temps de construire

Vous pouvez obtenir la déclaration d'une fonction dans votre actuel .rpc ou .h fichier par #incluant l'en-tête qui contient déjà une déclaration de la fonction. Cependant, cela peut ralentir votre compilation, surtout si vous #inclure un en-tête dans un .h au lieu de .rpc de votre programme, comme tout ce que #inclut le .h vous êtes en train de rédiger ce serait la fin de #inclure avec tous les les en-têtes que vous avez écrit #comprend trop. Soudain, le compilateur a #inclus des pages et des pages de code qu'il doit compiler même si vous ne voulais utiliser une ou deux fonctions. Pour éviter cela, vous pouvez utiliser une déclaration et il suffit de taper la déclaration de la fonction-vous en haut du fichier. Si vous utilisez seulement quelques fonctions, cela peut vraiment rendre vos compilations plus rapides par rapport à toujours #incluant l'en-tête. Pour de très grands projets, la différence peut être d'une heure ou plus de temps de compilation acheté en quelques minutes.

rompre les renvois cycliques lorsque deux définitions sont utilisées l'une l'autre

de plus, les déclarations forward peuvent vous aider à briser les cycles. C'est là que deux fonctions essayent de s'utiliser l'une l'autre. Lorsque cela se produit (et c'est une chose parfaitement valide à faire), vous pouvez #inclure un fichier d'en-tête, mais ce fichier d'en-tête essaie #d'inclure le fichier d'en-tête que vous écrivez actuellement.... qui alors # inclut l'autre en-tête, qui # inclut celui que vous écrivez. Vous êtes coincé dans une situation de poulet et d'œuf avec chaque fichier d'en-tête essayant de re #inclure l'autre. Pour résoudre cela, vous pouvez transmettre-déclarer les pièces dont vous avez besoin dans un des fichiers et laisser le #include hors de ce fichier.

par exemple:

Fichier Voiture.h

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

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

Fichier De Roue.h

Hmm... la déclaration de la voiture est exigée ici comme la roue a un indicateur à la voiture, mais la voiture.h NE peut pas être inclus ici car cela entraînerait une erreur de compilation. Si La Voiture.h a été inclus, qui essaierait ensuite D'inclure la roue.h qui inclurait la voiture.h qui inclurait la roue.h et ce serait aller à l'infini, de sorte qu'au lieu que le compilateur génère une erreur. La solution est de transmettre la voiture déclarer à la place:

class Car;     // forward declaration

class Wheel
{
    Car* car;
};

si la roue de classe avait des méthodes qui doivent appeler des méthodes de voiture, ces méthodes pourraient être définis dans la Roue.rpc et de la Roue.le rpc est maintenant en mesure d'inclure Voiture.h sans provoquer de cycle.

309
répondu Scott Langham 2015-04-02 05:20:10

le compilateur recherche chaque symbole utilisé dans l'Unité de traduction courante est déjà déclaré ou non dans l'unité courante. C'est juste une question de style toutes les signatures de méthode au début d'un fichier source, tandis que les définitions sont fournies plus tard. L'utilisation importante de cela est lorsque vous utilisez un pointeur vers une classe comme variable membre d'une autre classe.

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

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

ainsi, utilisez les déclarations forward dans les classes lorsque cela est possible. Si votre programme a des fonctions (avec des fichiers d'en-tête ho), alors fournir des prototypes au début n'est qu'une question de style. Ce serait de toute façon le cas si le fichier d'en-tête était présent dans un programme normal avec en-tête qui n'a que des fonctions.

24
répondu Mahesh 2011-01-21 10:31:00

parce que C++ est divisé du haut vers le bas, le compilateur a besoin de savoir sur les choses avant qu'elles ne soient utilisées. Donc, quand vous faites référence:

int add( int x, int y )

dans la fonction principale, le compilateur doit savoir qu'il existe. Pour le prouver essayez de le déplacer au-dessous de la fonction principale, et vous obtiendrez une erreur de compilation.

Donc un " Déclaration anticipée ' est juste ce qu'il indique sur l'étain. Il déclare quelque chose avant son utilisation.

en général, vous incluez les déclarations à terme dans un fichier d'en-tête et incluez ensuite ce fichier d'en-tête de la même manière que iostream est inclus.

11
répondu Nick 2011-01-21 10:16:44

Le terme " déclaration anticipée " en C++ est principalement utilisé pour déclarations de classe . Voir (la fin de) cette réponse pour savoir pourquoi une "déclaration forward" d'une classe n'est vraiment qu'une simple déclaration de classe avec un nom fantaisiste.

en d'autres termes, le "forward" ajoute simplement le ballast au terme, comme n'importe quel déclaration peut être vu comme étant vers l'avant dans la mesure où il déclare un identifiant avant il est utilisé.

(ce qui est un déclaration plutôt que définition , encore une fois voir Quelle est la différence entre une définition et une déclaration? )

9
répondu sbi 2017-05-23 10:31:39

quand le compilateur voit add(3, 4) il doit savoir ce que cela signifie. Avec la déclaration forward vous dites essentiellement au compilateur que add est une fonction qui prend deux ints et renvoie un int. C'est une information importante pour le compilateur parce qu'il a besoin de mettre 4 et 5 dans la représentation correcte sur la pile et il a besoin de savoir quel type la chose retournée par add est.

à ce moment-là, le compilateur n'est pas inquiet du réel mise en œuvre de add , c'est-à-dire où il est (ou s'il y a est même un) et s'il compile. Cela apparaît plus tard, après compilant les fichiers source lorsque le linker est invoqué.

1
répondu René Nyffenegger 2011-01-21 10:18:52
int add(int x, int y); // forward declaration using function prototype

pouvez-vous expliquer "" plus loin dans le temps? Quel est le problème si nous l'utilisons dans la fonction main ()?

c'est comme #include"add.h" . Si vous le savez,de préprocesseur élargit le fichier que vous mentionnez dans #include , dans le .cpp file où vous écrivez la directive #include . Cela signifie que, si vous écrivez #include"add.h" , vous obtenez la même chose, c'est comme si vous faire de la "déclaration anticipée".

je suppose que add.h a cette ligne:

int add(int x, int y); 
1
répondu Nawaz 2011-01-21 10:25:41

Une problème est, que le compilateur ne sait pas, quel type de valeur est fournie par votre fonction, est suppose que la fonction renvoie un int dans ce cas, mais cela peut être aussi correcte qu'elle peut être fausse. Un autre problème est, que le compilateur ne sait pas, quel type d'arguments votre fonction attend, et ne peut pas vous prévenir, si vous passez des valeurs du mauvais type. Il y a des règles spéciales de "promotion", qui s'appliquent en passant, par exemple, des valeurs de point flottant à un fonction non déclarée (le compilateur doit les élargir pour taper double), ce qui n'est souvent pas ce que la fonction attend réellement, conduisant à des bugs difficiles à trouver à l'exécution.

0
répondu Dirk 2011-01-21 10:18:03

un addendum rapide concernant: habituellement vous mettez ces références avancées dans un fichier d'en-tête appartenant à la .c(pp) fichier où la fonction/variable etc. est mis en œuvre. dans votre exemple, cela ressemblerait à ceci:: ajouter.h:

extern int add(int a, int b);

le mot-clé extern indique que la fonction est effectivement déclarée dans un fichier externe (pourrait également être une bibliothèque, etc.). votre principale.c ressemblerait à ceci:

#include 
#include "add.h"

int main()
{
.
.
.

0
répondu jack 2011-01-21 10:27:12