Quand puis-je utiliser une déclaration anticipée?
Je suis à la recherche de la définition de quand je suis autorisé à le faire avant la déclaration d'une classe dans une autre classe du fichier d'en-tête:
Suis-je autorisé à le faire pour une classe de base, pour une catégorie qu'il détient en tant que membre, pour une classe transmis à la fonction de membre par référence, etc. ?
- Je veux désespérément ce sera renommé "quand dois-je", et les réponses mis à jour de façon appropriée...
- Quand vous dites lors de la "devrait", vous demandez pour avis.
- c'est ma compréhension que vous souhaitez utiliser avant de déclarations chaque fois que vous pouvez, pour améliorer le temps de construction et d'éviter les références circulaires. La seule exception est, je pense, quand un fichier include contient les typedefs, auquel cas il y a un compromis à trouver entre la re-définition de la définition de type (et risquer de changer) et comprenant un dossier complet (avec son récursive comprend).
- À partir d'un point de vue pratique, je ne suis pas un grand fan des en-têtes de que mon. ÷
- toujours, fondamentalement, vous obliger à inclure un en-tête différent pour les utiliser (avant decl de paramètre du constructeur est un grand coupable ici)
- En règle générale dépendances circulaires sont mieux évités grâce à une meilleure conception de la programmation orientée objet - les exceptions s'appliquent toujours (par exemple, des visiteurs); ce qui répond à la "quand le devrais-je". Toutefois les exceptions aux règles sont des cas exceptionnels, afin de répondre à "quand je"; idéalement, rarement sans justification
Vous devez vous connecter pour publier un commentaire.
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 de 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.
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:
Déclarer des méthodes ou des fonctions qui acceptent/retour incomplet types:
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):
Ce que vous ne pouvez pas faire avec un type incomplète:
L'utiliser comme une classe de base
L'utiliser pour déclarer un membre:
Définir des méthodes ou des fonctions à l'aide de ce type
Utiliser ses méthodes ou des champs, en fait en essayant de déréférencer une variable de type incomplète
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>
exige de ses paramètre pour être complet, tandis que leboost::container::vector<T>
ne le fait pas. Parfois, un type est nécessaire que si vous utilisez certaines fonctions de membre; c'est le cas pourstd::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.
new
opérateur? Est-il besoin de connaître le type complètes? par exemple,X * myFunc(){ return new X; }
const X* do_something( void )
?boost::shared_ptr
: Le modèle de classe est paramétrée sur T, le type de l'objet pointé. shared_ptr et la plupart de ses fonctions de membre place des exigences en matière de T; il est autorisé à être un type incomplète ou nulle.as long as the body of the function does not need the definition
Ma conjecture est que @Dilawar signifiait vraiment "déclarer" quand ils ont dit "définir", tout en demandant au sujet de ce que leur fonction pourrait le faire avec un type incomplète. Dans ce cas, ladite fonction de la déclaration est probablement dans un en-tête et la définition est probablement dans unecpp
fichier, celui-ci peut#include
/définir le type complètes et l'utiliser pour tout.La règle principale est que vous pouvez seulement de l'avant-déclarer des classes dont la disposition de la mémoire (et donc les fonctions de membres et de membres de données) n'ont pas besoin d'être connue dans le fichier de l'avant-déclarer.
Cela permettrait d'exclure de la base de classes et tout, mais les classes utilisées par les références et les pointeurs.
Lakos distingue entre la classe d'utilisation
Je ne l'ai jamais vu prononcée de façon plus succincte 🙂
Ainsi que des pointeurs et des références incomplètes types, vous pouvez également déclarer les prototypes de fonction qui spécifient les paramètres et/ou des valeurs de retour incomplet types. Cependant, vous ne pouvez pas définir une fonction ayant un paramètre ou le type de retour est incomplète, sauf s'il s'agit d'un pointeur ou d'une référence.
Exemples:
Aucune réponse jusqu'à présent décrire quand on peut utiliser une déclaration anticipée d'un modèle de classe. Donc, ici il va.
Un modèle de classe peuvent être transmises, a déclaré que:
En suivant la structure de la accepté de répondre à,
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 dans un autre modèle de classe:
Déclarer un membre à un pointeur ou une référence à l'un de ses incomplète instanciations:
Déclarer les modèles de fonction ou fonction membre modèles qui acceptent/retour incomplet types:
De déclarer des fonctions ou des fonctions membres qui acceptent/retour de l'un de ses incomplète instanciations:
Définir les modèles de fonction ou fonction membre modèles qui acceptent de rendement/pointeurs/références pour le type incomplète (mais sans l'aide de ses membres):
Définir des méthodes ou des fonctions qui acceptent de rendement/pointeurs/références pour l'un de ses incomplète instanciations (mais sans l'aide de ses membres):
L'utiliser comme une classe de base d'un autre modèle de classe
L'utiliser pour déclarer un membre d'un autre modèle de classe:
Définir les modèles de fonction ou de méthodes à l'aide de ce type
Ce que vous ne pouvez pas faire avec un type incomplète:
Utiliser l'un de ses instanciations comme une classe de base
Utiliser l'un de ses instanciations de déclarer un membre:
Définir des méthodes ou des fonctions à l'aide de l'un de ses instanciations
Utiliser les méthodes ou des domaines de l'une de ses instanciations, en fait en essayant de déréférencer une variable de type incomplète
Créer explicite les instanciations de la classe de modèle
X
etX<int>
sont exactement les mêmes, seul l'avant-déclarant syntaxe diffère de quelque façon substantielle, avec tous mais 1 ligne de votre réponse, s'élevant à prendre simplement Luc ets/X/X<int>/g
? Est-ce vraiment nécessaire? Ou ai-je oublié un petit détail qui est différent? C'est possible, mais j'ai visuellement par rapport à quelques reprises et ne peut pas voir tout...Dans le fichier dans lequel vous utilisez uniquement le Pointeur ou une Référence à une classe.Et aucun membre/membre de la fonction doit être appelée pensé à ceux Pointeur/référence.
avec
class Foo;
//déclaration anticipéeNous pouvons déclarer les membres de données de type Foo* ou Foo&.
Nous pouvons déclarer (mais pas de définir des fonctions avec des arguments, et/ou des valeurs de retour de type Foo.
Nous pouvons déclarer des données membres statiques de type Foo. C'est parce que les données membres statiques sont définis en dehors de la définition de la classe.
Je suis en train d'écrire cela comme une réponse distincte plutôt que juste un commentaire parce que je suis en désaccord avec Luc Touraille réponse, pas sur le terrain de la légalité, mais pour les logiciels robustes et le risque d'interprétation erronée.
Précisément, j'ai un problème avec le contrat implicite de ce que vous vous attendez à ce que les utilisateurs de votre interface d'avoir à connaître.
Si vous êtes de retour ou d'accepter des types de référence, alors vous êtes juste de dire qu'ils peuvent passer à travers un pointeur ou une référence sur lequel ils peuvent à leur tour sont connus qu'à travers une déclaration anticipée.
Lorsque vous êtes de retour incomplet, ce type de
X f2();
alors que vous dites à votre interlocuteur doit ont la pleine spécification du type de X. Ils ont besoin pour créer de la LHS ou temporaire de l'objet sur le site d'appel.De même, si vous acceptez un type incomplète, l'appelant doit avoir construit l'objet qui est le paramètre. Même si cet objet a été retourné comme un autre type incomplète à partir d'une fonction, le site d'appel a besoin de l'intégralité de la déclaration. c'est à dire:
Je pense qu'il y a un principe important qui un en-tête doit fournir suffisamment d'informations pour l'utiliser sans une dépendance nécessitant d'autres en-têtes. Cela signifie que l'en-tête doit être capable d'être inclus dans une unité de compilation sans provoquer une erreur de compilation lorsque vous utilisez toutes les fonctions qu'il déclare.
Sauf
Si cette dépendance externe est souhaité comportement. Au lieu d'utiliser la compilation conditionnelle, vous pouvez avoir un bien documenté obligation de fournir leur propre en-tête déclarant X. C'est une alternative à l'utilisation de #ifdefs et peut être un moyen utile d'introduire des objets fantaisie ou d'autres variantes.
La distinction importante en cours de certains modèle de techniques où vous sont explicitement PAS prévu de les instancier, mentionné juste pour quelqu'un ne reçoit pas désagréable avec moi.
I disagree with Luc Touraille's answer
Donc lui écrire un commentaire, y compris un lien vers un article de blog si vous avez besoin de la longueur. Cela ne veut pas répondre à la question posée. Si tout le monde pensait questions sur la façon dont X fonctionne justifiée réponses en désaccord avec X faire débattre les limites dans lesquelles nous devons restreindre notre liberté d'utiliser X - nous aurions presque pas de vraies réponses.La règle générale que j'ai suivi est de ne pas inclure le fichier d'en-tête, à moins que je. Donc, sauf si je suis de ranger l'objet d'une classe comme une variable de membre de ma classe je ne comprend pas, je vais juste utiliser la déclaration anticipée.
Aussi longtemps que vous n'avez pas besoin de la définition (pensez à les pointeurs et les références), vous pouvez vous en sortir avec sa déclaration. C'est pourquoi le plus souvent, vous devriez voir dans les en-têtes tandis que les fichiers de mise en œuvre généralement va tirer l'en-tête de la définition appropriée(s).
Vous aurez en général, l'utilisation de l'avant dans la déclaration de classes fichier d'en-tête lorsque vous souhaitez utiliser l'autre type (classe) en tant que membre de la classe. Vous ne pouvez pas utiliser l'avant-classes déclarées méthodes dans le fichier d'en-tête parce que le C++ ne connaissez pas la définition de cette classe à ce moment encore. C'est logique, vous devez vous déplacer dans le .rpc-fichiers, mais si vous utilisez un modèle de fonctions, vous devez les réduire à la partie qui utilise le modèle et déplacer cette fonction dans l'en-tête.
Le prendre avant la déclaration d'obtenir votre code à la compilation (obj est créé). Liant toutefois (exe création) ne sera pas réussie à moins que les définitions sont trouvés.
class A; class B { A a; }; int main(){}
, et laissez-moi savoir comment ça se passe. Bien sûr, il ne compile pas. Toutes les réponses ici d'expliquer pourquoi et précis, limité contextes dans lesquels l'avant-déclaration de est valide. Au lieu de cela vous avez écrit ceci à propos de quelque chose de totalement différent.Je veux juste ajouter une chose importante que vous pouvez faire avec un envoyé de classe ne sont pas mentionnés dans la réponse de Luc Touraille.
Ce que vous pouvez faire avec un type incomplète:
Définir des méthodes ou des fonctions qui acceptent/retour
pointeurs/références pour le type incomplète et avant que les pointeurs/références
à une autre fonction.
Un module peut passer à travers un objet d'déclaré classe à un autre module.
Que, Luc Touraille a déjà expliqué qu'il est très bien où l'utiliser et ne pas utiliser avant la déclaration de la classe.
Je vais juste ajouter à ce pourquoi nous avons besoin de l'utiliser.
Nous devrions être en Avant de la déclaration dans la mesure du possible pour éviter les indésirables de l'injection de dépendances.
Comme
#include
fichiers d'en-tête sont ajoutés sur plusieurs fichiers par conséquent, si l'on ajoute un en-tête dans un autre fichier d'en-tête, il va ajouter indésirables de l'injection de dépendances dans les différentes parties de code source qui peut être évité en ajoutant#include
l'en-tête dans.cpp
fichiers dans la mesure du possible plutôt que de les ajouter à un autre fichier d'en-tête et de l'utilisation de la classe avant la déclaration dans la mesure du possible dans l'en-tête.h
fichiers.