Quelqu'un peut-il s'il vous plaît me diriger vers de belles ressources pour comprendre et utiliser des classes imbriquées? J'ai du matériel comme les principes de programmation et des choses comme ce centre de connaissances IBM - Classes imbriquées
Mais j'ai toujours du mal à comprendre leur but. Quelqu'un pourrait-il m'aider s'il vous plaît?
c++
nested
inner-classes
à lunettes
la source
la source
typedef
. 3. parce qu'ils ajoutent un niveau supplémentaire d'indentation dans un environnement où éviter les longues lignes est déjà difficile 4. parce que vous déclarez deux objets conceptuellement distincts dans une seuleclass
déclaration, etc.Réponses:
Les classes imbriquées sont cool pour cacher les détails de l'implémentation.
Liste:
Ici, je ne veux pas exposer Node car d'autres personnes pourraient décider d'utiliser la classe et cela m'empêcherait de mettre à jour ma classe car tout ce qui est exposé fait partie de l'API publique et doit être maintenu pour toujours . En rendant la classe privée, je cache non seulement l'implémentation, je dis aussi que c'est la mienne et je peux la changer à tout moment pour que vous ne puissiez pas l'utiliser.
Regardez
std::list
oustd::map
ils contiennent tous des classes cachées (ou le font-ils?). Le fait est qu'ils peuvent ou non, mais parce que l'implémentation est privée et cachée, les constructeurs de la STL ont pu mettre à jour le code sans affecter la façon dont vous avez utilisé le code, ou laisser beaucoup de vieux bagages traînant autour de la STL parce qu'ils ont besoin pour maintenir la compatibilité ascendante avec un imbécile qui a décidé d'utiliser la classe Node qui était cachée à l'intérieurlist
.la source
Node
ne devrait pas du tout être exposé dans le fichier d'en-tête.detail
convention: au lieu de cela, en fonction de ces conventions, il faut se souvenir, il vaut mieux dépendre du compilateur qui en garde la trace pour vous.Les classes imbriquées sont comme les classes normales, mais:
Quelques exemples:
Classe d'imbrication publique pour la placer dans une portée de classe pertinente
Supposons que vous souhaitiez avoir une classe
SomeSpecificCollection
qui regrouperait des objets de classeElement
. Vous pouvez alors soit:déclarer deux classes:
SomeSpecificCollection
etElement
- mauvais, car le nom "Element" est assez général pour provoquer un éventuel conflit de nomintroduire un espace de noms
someSpecificCollection
et déclarer les classessomeSpecificCollection::Collection
etsomeSpecificCollection::Element
. Pas de risque de clash de nom, mais peut-il devenir plus bavard?déclarer deux classes globales
SomeSpecificCollection
etSomeSpecificCollectionElement
- ce qui a des inconvénients mineurs, mais c'est probablement OK.déclarer la classe globale
SomeSpecificCollection
et la classeElement
comme sa classe imbriquée. Ensuite:SomeSpecificCollection
vous faites référence à justeElement
, et partout ailleurs commeSomeSpecificCollection::Element
- ce qui ressemble + - à la même chose que 3., mais plus clairSomeSpecificCollection
c'est aussi une classe.À mon avis, la dernière variante est certainement la conception la plus intuitive et donc la meilleure.
Permettez-moi de souligner - Ce n'est pas une grande différence de créer deux classes globales avec des noms plus verbeux. C'est juste un tout petit détail, mais à mon avis, cela rend le code plus clair.
Présentation d'une autre portée dans une portée de classe
Ceci est particulièrement utile pour introduire des typedefs ou des enums. Je vais juste poster un exemple de code ici:
On appellera alors:
Mais quand on regarde les propositions de complétion de code pour
Product::
, on obtiendra souvent toutes les valeurs d'énumération possibles (BOX, FANCY, CRATE) et il est facile de faire une erreur ici (les énumérations fortement typées de C ++ 0x résolvent en quelque sorte cela, mais tant pis ).Mais si vous introduisez une portée supplémentaire pour ces énumérations en utilisant des classes imbriquées, les choses pourraient ressembler à ceci:
Ensuite, l'appel ressemble à:
Ensuite, en tapant
Product::ProductType::
un IDE, on obtiendra uniquement les énumérations de la portée souhaitée suggérée. Cela réduit également le risque de faire une erreur.Bien sûr, cela n'est peut-être pas nécessaire pour les petites classes, mais si l'on a beaucoup d'énumérations, cela facilite les choses pour les programmeurs clients.
De la même manière, vous pouvez «organiser» un grand nombre de typedefs dans un modèle, si jamais vous en aviez besoin. C'est parfois un modèle utile.
L'idiome PIMPL
Le PIMPL (abréviation de Pointer to IMPLementation) est un idiome utile pour supprimer les détails d'implémentation d'une classe de l'en-tête. Cela réduit le besoin de recompiler les classes en fonction de l'en-tête de la classe chaque fois que la partie "implémentation" de l'en-tête change.
Il est généralement implémenté à l'aide d'une classe imbriquée:
Xh:
X.cpp:
Ceci est particulièrement utile si la définition de classe complète a besoin de la définition de types à partir d'une bibliothèque externe qui a un fichier d'en-tête lourd ou juste moche (prenez WinAPI). Si vous utilisez PIMPL, vous pouvez inclure une fonctionnalité spécifique à WinAPI uniquement dans
.cpp
et ne jamais l'inclure dans.h
.la source
struct Impl; std::auto_ptr<Impl> impl;
Cette erreur a été popularisée par Herb Sutter. N'utilisez pas auto_ptr sur des types incomplets, ou au moins prenez des précautions pour éviter la génération de code erroné.auto_ptr
type incomplet dans la plupart des implémentations, mais techniquement, c'est UB contrairement à certains des modèles de C ++ 0x (par exempleunique_ptr
) où il a été précisé que le paramètre de modèle peut être un type incomplet et où exactement le type doit être complet. (par exemple, utilisation de~unique_ptr
)T
de modèle deunique_ptr
peut être un type incomplet."enum class
.Je n'utilise pas beaucoup les classes imbriquées, mais je les utilise de temps en temps. Surtout quand je définis une sorte de type de données, et que je veux ensuite définir un foncteur STL conçu pour ce type de données.
Par exemple, considérons une
Field
classe générique qui a un numéro d'identification, un code de type et un nom de champ. Si je veux rechercher unvector
de cesField
s par numéro d'identification ou par nom, je pourrais construire un foncteur pour le faire:Ensuite, le code qui a besoin de rechercher ces
Field
s peut utiliser lamatch
portée dans laField
classe elle-même:la source
On peut implémenter un modèle Builder avec une classe imbriquée . Surtout en C ++, personnellement je le trouve sémantiquement plus propre. Par exemple:
Plutôt que:
la source