643 votes

Quand puis-je utiliser une déclaration forward?

Je cherche la définition de quand je suis autorisé à faire la déclaration avant d'une classe dans le dossier d'en-tête d'une autre classe:

Suis-je autorisé à le faire pour une classe de base, pour une classe tenue en tant que membre, pour une classe transmise à une fonction membre par référence, etc.?

1024voto

Luc Touraille Points 29252

Mettez-vous dans le compilateur de position: lorsque vous transférez déclarer un type, tout le compilateur sait, c'est que ce type existe; il ne sait rien au sujet de sa taille, les membres ou les méthodes. C'est pourquoi il est appelé un type incomplète. Par conséquent, vous ne pouvez pas utiliser le type de déclarer un membre, ou d'une classe de base, puisque le compilateur aurait besoin de savoir la mise en page de ce type.

En supposant que le terme suivantes déclaration.

class X;

Voici ce que vous pouvez faire et ne pas faire.

Ce que vous pouvez faire avec un type incomplète:

  • Déclarer un membre d'un pointeur ou d'une référence à la nature incomplète de type:

    class Foo {
        X *pt;
        X &pt;
    };
    
  • Déclarer des méthodes ou des fonctions qui acceptent/retour incomplet types:

    void f1(X);
    X    f2();
    
  • Définir des méthodes ou des fonctions qui acceptent de rendement/pointeurs/références pour le type incomplète (mais sans l'aide de ses membres):

    void f3(X*, X&) {}
    X&   f4()       {}
    X*   f5()       {}
    

Ce que vous ne pouvez pas faire avec un type incomplète:

  • L'utiliser comme une classe de base

    class Foo : X {} // compiler error!
    
  • L'utiliser pour déclarer un membre:

    class Foo {
        X m; // compiler error!
    };
    
  • Définir des méthodes ou des fonctions à l'aide de ce type

    void f1(X x) {} // compiler error!
    X    f2()    {} // compiler error!
    
  • Utiliser ses méthodes ou des champs, en fait en essayant de déréférencer une variable de type incomplète

    class Foo {
        X *m;            
        void method()            
        {
            m->someMethod();      // compiler error!
            int i = m->someField; // compiler error!
        }
    };
    

Quand il s'agit de modèles, il n'y a pas de règle absolue: si vous pouvez utiliser un type incomplète comme un paramètre du modèle dépend de la façon dont le type est utilisé dans le modèle.

Par exemple, std::vector<T> nécessite son paramètre pour être complet, tandis que le boost::container::vector<T> ne le sont pas. Parfois, un type est nécessaire que si vous utilisez certaines fonctions de membre; c'est le cas pour std::unique_ptr<T>, par exemple.

Bien documentée modèle doit indiquer dans sa documentation à toutes les exigences de ses paramètres, y compris la nécessité de compléter les types ou pas.

49voto

Timo Geusch Points 16952

La règle principale est que vous ne pouvez déclarer que des classes dont la mise en forme de la mémoire (et donc les fonctions membres et les membres de données) n'ont pas besoin d'être connues dans le fichier que vous retransmettez.

Cela exclurait les classes de base et tout sauf les classes utilisées via des références et des pointeurs.

34voto

Marc Mutz - mmutz Points 10367

Lakos distingue entre l'utilisation en classe

  1. nom unique (pour lequel une déclaration anticipée est suffisante) et
  2. in-size (pour lequel la définition de classe est nécessaire).

Je ne l'ai jamais vu prononcé plus succinctement :)

29voto

j_random_hacker Points 28473

Outre les pointeurs et les références aux types incomplets, vous pouvez également déclarer des prototypes de fonction qui spécifient des paramètres et / ou des valeurs de retour qui sont des types incomplets. Cependant, vous ne pouvez pas définir une fonction dont le paramètre ou le type de retour est incomplet, sauf s'il s'agit d'un pointeur ou d'une référence.

Exemples:

 struct X;              // Forward declaration of X

void f1(X* px) {}      // Legal: can always use a pointer/reference
X f2(int);             // Legal: return value in function prototype
void f3(X);            // Legal: parameter in function prototype
void f4(X) {}          // ILLEGAL: *definitions* require complete types
 

3voto

dirkgently Points 56879

Tant que vous n’avez pas besoin de la définition (pensez-vous pointeurs et références), vous pouvez sortir avec déclarations anticipées. C’est pourquoi la plupart du temps vous le voyait dans les en-têtes tandis que les fichiers de mise en œuvre en général tirera l’en-tête pour les définitions appropriées.

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