291 votes

C++ - Déclaration en avant

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

Les éléments suivants sont mentionnés :

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 avions utilisé une déclaration forward pour que le compilateur sache ce qu'était add lors de la compilation de main.cpp. Comme mentionné précédemment, écrire des déclarations forward pour chaque fonction que vous voulez utiliser et qui se trouve dans d'autres fichiers peut devenir rapidement fastidieux.

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

Merci.

480voto

Scott Langham Points 17447

Pourquoi le forward-declare 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. Il insiste donc pour voir une déclaration de "add" (ou de tout autre type, classe ou fonction) avant de l'utiliser.

Cela permet simplement au compilateur de mieux valider le code et de mettre de l'ordre dans les détails afin de produire un fichier objet bien ordonné. Si vous n'aviez pas à déclarer les choses, le compilateur produirait un fichier objet qui devrait contenir des informations sur toutes les suppositions possibles sur ce que la fonction 'add' pourrait être. Et le linker devrait contenir une logique très intelligente pour essayer de déterminer quelle fonction 'add' vous avez réellement l'intention d'appeler, alors que la fonction 'add' peut se trouver dans un fichier objet différent que le linker joint à celui qui utilise add pour produire une dll ou un exe. Il est possible que l'éditeur de liens obtienne le mauvais add. Disons que vous vouliez utiliser int add(int a, float b), mais que vous avez accidentellement oublié de l'écrire, mais que le linker a trouvé un int add(int a, int b) déjà existant et a pensé que c'était le bon et l'a utilisé à la place. Votre code serait compilé, mais ne ferait pas ce que vous attendiez.

Ainsi, afin de garder les choses explicites et d'éviter les devinettes, le compilateur insiste pour que vous déclariez tout avant de l'utiliser.

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 convocation, le nom de la méthode, les arguments et leurs types. Mais le code de la méthode n'est pas nécessaire. Pour une définition, vous avez besoin de la déclaration et du code de la fonction.

Comment les déclarations préalables peuvent réduire considérablement les temps de construction

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 d'un .cpp de votre programme, car tout ce qui #inclut le .h que vous écrivez finira par #inclure tous les en-têtes pour lesquels vous avez écrit des #includes. Soudainement, le compilateur a #inclus des pages et des pages de code qu'il doit compiler même si vous ne vouliez utiliser qu'une ou deux fonctions. Pour éviter cela, vous pouvez utiliser une déclaration en avant et taper vous-même la déclaration de la fonction en haut du fichier. Si vous n'utilisez que quelques fonctions, cela peut vraiment accélérer la compilation par rapport à l'inclusion systématique de l'en-tête. Pour les projets de grande envergure, la différence peut représenter une heure ou plus de temps de compilation, ramenée à quelques minutes.

Rompre les références cycliques où deux définitions s'utilisent l'une l'autre

De plus, les déclarations en avant peuvent vous aider à briser les cycles. C'est le cas lorsque deux fonctions essaient de s'utiliser l'une l'autre. Lorsque cela se produit (et c'est une chose parfaitement valable), vous pouvez #inclure un fichier d'en-tête, mais ce fichier d'en-tête tente de #inclure le fichier d'en-tête que vous êtes en train d'écrire.... qui #inclut alors l'autre en-tête, qui #inclut celui que vous êtes en train d'écrire. Vous êtes coincé dans une situation de poule et d'œuf, chaque fichier d'en-tête essayant de re #inclure l'autre. Pour résoudre ce problème, vous pouvez déclarer les parties dont vous avez besoin dans l'un des fichiers et laisser le #include en dehors de ce fichier.

Eg :

Fichier Car.h

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

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

Fichier Wheel.h

Hmm... la déclaration de Car est nécessaire ici car Wheel a un pointeur sur une Car, mais Car.h ne peut pas être inclus ici car cela entraînerait une erreur de compilation. Si Car.h était inclus, il essaierait alors d'inclure Wheel.h qui inclurait Car.h qui inclurait Wheel.h et cela continuerait indéfiniment, donc le compilateur lève une erreur. La solution est de déclarer Car à la place :

class Car;     // forward declaration

class Wheel
{
    Car* car;
};

Si la classe Wheel a des méthodes qui doivent appeler des méthodes de Car, ces méthodes peuvent être définies dans Wheel.cpp et Wheel.cpp est maintenant capable d'inclure Car.h sans causer un cycle.

29voto

Mahesh Points 20994

Le compilateur recherche si chaque symbole utilisé dans l'unité de traduction en cours est déjà déclaré ou non dans l'unité en cours. Il s'agit simplement d'une question de style en fournissant toutes les signatures de méthodes au début d'un fichier source, tandis que les définitions sont fournies plus tard. L'utilisation la plus importante est celle qui consiste à utiliser 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"

Par conséquent, utilisez les déclarations prospectives dans les classes lorsque cela est possible. Si votre programme n'a que des fonctions (avec des fichiers d'en-tête), alors fournir des prototypes au début est juste 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.

15voto

Nick Points 9187

Comme le 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 à :

int add( int x, int y )

dans la fonction principale, le compilateur doit savoir qu'elle existe. Pour le prouver, essayez de le déplacer en dessous de la fonction principale et vous obtiendrez une erreur du compilateur.

Donc, un ' Déclaration prospective est exactement ce qui est écrit sur la boîte. C'est déclarer quelque chose avant de l'utiliser.

En général, vous incluez les déclarations prospectives dans un fichier d'en-tête, puis vous incluez ce fichier d'en-tête de la même manière que les déclarations prospectives de l'utilisateur. iostream est inclus.

14voto

sbi Points 100828

Le terme " déclaration prospective "en C++ n'est généralement utilisé que pour déclarations de classes . Voir (la fin de) cette réponse pour expliquer pourquoi une "déclaration directe" d'une classe n'est en fait qu'une simple déclaration de classe avec un nom fantaisiste.

En d'autres termes, le "forward" ne fait qu'ajouter du lest au terme, car tout On peut considérer que la déclaration est prospective dans la mesure où elle déclare un identifiant avant il est utilisé.

(Quant à ce qu'est un déclaration par opposition à un définition voir aussi Quelle est la différence entre une définition et une déclaration ? )

1voto

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

Pouvez-vous expliquer "déclaration préalable" plus en détail ? Quel est le problème si si nous l'utilisons dans la fonction main() ?

C'est la même chose que #include"add.h" . Si vous le savez, le préprocesseur développe le fichier que vous mentionnez dans la section #include dans le fichier .cpp où vous écrivez l'option #include de la 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 a cette ligne :

int add(int x, int y);

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X