À: http://www.learncpp.com/cpp-tutorial/19-header-files/
Ce qui suit est mentionné:
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;
}
Nous avons utilisé une déclaration avant pour que le compilateur sache ce qu'était "
add
" lors de la compilationmain.cpp
. Comme mentionné précédemment, l'écriture de déclarations avancées pour chaque fonction que vous souhaitez utiliser et qui réside dans un autre fichier peut devenir fastidieuse rapidement.
Pouvez-vous expliquer davantage la " déclaration à terme "? Quel est le problème si nous l'utilisons dans la main()
fonction?
c++
declaration
forward-declaration
Simplicité
la source
la source
Réponses:
Pourquoi la déclaration directe est nécessaire en C ++
Le compilateur veut s'assurer que vous n'avez pas fait d'erreurs 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 «add» (ou tout autre type, classe ou fonction) avant d'être utilisé.
Cela permet simplement au compilateur de faire un meilleur travail de validation du code, et lui permet de ranger les extrémités lâches afin qu'il puisse produire un fichier objet soigné. Si vous n'aviez pas à transmettre de déclaration, le compilateur produirait un fichier objet qui devrait contenir des informations sur toutes les suppositions possibles quant à ce que la fonction «ajouter» pourrait être. Et l'éditeur de liens devrait contenir une logique très intelligente pour essayer de déterminer quel «ajout» vous aviez réellement l'intention d'appeler, lorsque la fonction «ajouter» peut vivre dans un fichier objet différent que l'éditeur de liens joint à celui qui utilise ajouter pour produire une DLL ou un exe. Il est possible que l'éditeur de liens obtienne le mauvais ajout. Supposons que vous vouliez utiliser int add (int a, float b), mais que vous avez accidentellement oublié de l'écrire, mais l'éditeur de liens 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 compilerait, mais ne ferait pas ce que vous attendiez.
Donc, juste pour garder les choses explicites et éviter les suppositions, etc., le compilateur insiste pour que vous déclariez tout avant qu'il ne soit utilisé.
Différence entre déclaration et définition
En passant, 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, la convention d'appel, le nom de la méthode, les arguments et leurs types. Mais le code de la méthode n'est pas requis. Pour une définition, vous avez également besoin de la déclaration et du code de la fonction.
Comment les déclarations prévisionnelles peuvent réduire considérablement les temps de génération
Vous pouvez obtenir la déclaration d'une fonction dans votre fichier .cpp ou .h actuel en # incluant l'en-tête qui contient déjà une déclaration de la fonction. Cependant, cela peut ralentir votre compilation, surtout si vous # incluez un en-tête dans un .h au lieu de .cpp de votre programme, car tout ce qui #inclut le .h que vous écrivez finirait par # inclure tous les en-têtes vous avez également écrit #includes pour. Du coup, le compilateur a #inclus des pages et des pages de code dont il a besoin pour compiler même lorsque vous ne souhaitez utiliser qu'une ou deux fonctions. Pour éviter cela, vous pouvez utiliser une déclaration directe et tapez simplement la déclaration de la fonction vous-même en haut du fichier. Si vous n'utilisez que quelques fonctions, cela peut vraiment rendre vos compilations plus rapides que de toujours inclure # l'en-tête. Pour les très gros projets,
Briser les références cycliques où deux définitions utilisent les deux
De plus, les déclarations à terme peuvent vous aider à rompre les cycles. C'est là que deux fonctions essaient toutes deux de s'utiliser. Lorsque cela se produit (et c'est une chose parfaitement valable à faire), vous pouvez #inclure un fichier d'en-tête, mais ce fichier d'en-tête essaie de #inclure le fichier d'en-tête que vous écrivez actuellement .... qui #inclut ensuite l'autre en-tête , qui #inclut celui que vous écrivez. Vous êtes coincé dans une situation de poulet et d'oeuf avec chaque fichier d'en-tête essayant de ré-inclure l'autre. Pour résoudre ce problème, vous pouvez déclarer les pièces dont vous avez besoin dans l'un des fichiers et laisser le #include hors de ce fichier.
Par exemple:
Fichier Car.h
File Wheel.h
Hmm ... la déclaration de Car est requise ici car Wheel a un pointeur sur Car, mais Car.h ne peut pas être inclus ici car cela entraînerait une erreur de compilation. Si Car.h était inclus, cela essaierait alors d'inclure Wheel.h qui inclurait Car.h qui inclurait Wheel.h et cela continuerait pour toujours, donc à la place le compilateur soulève une erreur. La solution est de transmettre à la place déclarer Car:
Si la classe Wheel avait des méthodes qui doivent appeler des méthodes de car, ces méthodes pourraient être définies dans Wheel.cpp et Wheel.cpp est maintenant capable d'inclure Car.h sans provoquer de cycle.
la source
// From Car.h
vous pouvez créer des situations velues en essayant de trouver une définition sur la route, c'est garanti.Le compilateur recherche que chaque symbole utilisé dans l'unité de traduction actuelle est précédemment déclaré ou non dans l'unité actuelle. C'est juste une question de style fournissant toutes les signatures de méthode au début d'un fichier source tandis que les définitions sont fournies plus tard. Son utilisation importante est lorsque vous utilisez un pointeur vers une classe en tant que variable membre d'une autre classe.
Donc, utilisez des déclarations directes dans les classes lorsque cela est possible. Si votre programme n'a que des fonctions (avec des fichiers d'en-tête ho), 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 un en-tête qui n'a que des fonctions.
la source
Parce que C ++ est analysé de haut en bas, le compilateur doit connaître les choses avant de les utiliser. Ainsi, lorsque vous faites référence:
dans la fonction principale, le compilateur doit savoir qu'il existe. Pour le prouver, essayez de le déplacer en dessous de la fonction principale et vous obtiendrez une erreur de compilation.
Donc, une « déclaration prospective » est exactement ce qu'elle dit sur l'étain. Il déclare quelque chose avant son utilisation.
En règle générale, vous incluez des déclarations directes dans un fichier d'en-tête, puis vous incluez ce fichier d'en-tête de la même manière que iostream est inclus.
la source
Le terme " déclaration avant " en C ++ est principalement utilisé uniquement pour les déclarations de classe . Voir (la fin de) cette réponse pour savoir pourquoi une "déclaration avant" d'une classe est vraiment juste une simple déclaration de classe avec un nom de fantaisie.
En d'autres termes, le "forward" ajoute simplement du lest au terme, car toute déclaration peut être vue comme étant en avant dans la mesure où elle déclare un identifiant avant d' être utilisée.
(En ce qui concerne ce qu'est une déclaration par opposition à une définition , voir à nouveau Quelle est la différence entre une définition et une déclaration? )
la source
Lorsque le compilateur le voit,
add(3, 4)
il doit savoir ce que cela signifie. Avec la déclaration directe, vous dites au compilateur qu'iladd
s'agit d'une fonction qui prend deux entrées et retourne un entier. Il s'agit d'informations importantes pour le compilateur car il doit mettre 4 et 5 dans la représentation correcte sur la pile et doit savoir de quel type est la chose retournée par add.À ce moment - là, le compilateur n'est pas inquiet au sujet de la réelle mise en œuvre
add
, à savoir où il est (ou s'il est encore un) et si elle compile. Cela apparaît plus tard, après avoir compilé les fichiers source lorsque l'éditeur de liens est appelé.la source
C'est la même chose que
#include"add.h"
. Si vous le savez, le préprocesseur développe le fichier que vous mentionnez#include
dans le fichier .cpp où vous écrivez la#include
directive. Cela signifie que si vous écrivez#include"add.h"
, vous obtenez la même chose, c'est comme si vous faisiez une "déclaration avant".Je suppose que
add.h
cette ligne a:la source
un petit addenda concernant: habituellement vous mettez ces références avancées dans un fichier d'en-tête appartenant au fichier .c (pp) où la fonction / variable etc. est implémentée. dans votre exemple, cela ressemblerait à ceci: add.h:
le mot-clé extern indique que la fonction est effectivement déclarée dans un fichier externe (peut également être une bibliothèque, etc.). votre main.c ressemblerait à ceci:
la source
Un problème est que le compilateur ne sait pas quel type de valeur est fourni par votre fonction; est suppose que la fonction renvoie un
int
dans ce cas, mais cela peut être aussi correct que faux. Un autre problème est que le compilateur ne sait pas quel type d'arguments votre fonction attend et ne peut pas vous avertir si vous transmettez des valeurs de mauvais type. Il existe des règles spéciales de "promotion", qui s'appliquent lors du passage, par exemple, de valeurs à virgule flottante à une 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, ce qui conduit à des bogues difficiles à trouver lors de l'exécution.la source