Les avantages et les inconvénients de l'utilisation de classes et d'énumérations c++ imbriquées?
Quels sont les avantages et les inconvénients de l'utilisation de classes publiques enchevêtrées C++ et des énumérations? Par exemple, supposons que vous ayez une classe appelée printer
, et que cette classe stocke également des informations sur les plateaux de sortie, vous pourriez avoir:
class printer
{
public:
std::string name_;
enum TYPE
{
TYPE_LOCAL,
TYPE_NETWORK,
};
class output_tray
{
...
};
...
};
printer prn;
printer::TYPE type;
printer::output_tray tray;
alternativement:
class printer
{
public:
std::string name_;
...
};
enum PRINTER_TYPE
{
PRINTER_TYPE_LOCAL,
PRINTER_TYPE_NETWORK,
};
class output_tray
{
...
};
printer prn;
PRINTER_TYPE type;
output_tray tray;
je peux voir les avantages de l'emboîtement des enum/classes privées, mais quand il s'agit des enum / classes publiques, le bureau est divisé - il semble être plus d'un choix de style.
alors, qu'est-ce que vous préférez et pourquoi?
13 réponses
classes imbriquées
il y a plusieurs effets secondaires aux classes imbriquées à l'intérieur des classes que je considère généralement comme des défauts (si ce n'est pas de purs antipatterns).
imaginons le code suivant:
class A
{
public :
class B { /* etc. */ } ;
// etc.
} ;
Ou encore:
class A
{
public :
class B ;
// etc.
} ;
class A::B
{
public :
// etc.
} ;
:
- accès privilégié: A:: b a privilégié l'accès à tous les membres de A (méthodes, variables, symboles, etc.), ce qui affaiblit l'encapsulation
- la portée de A est candidat pour la recherche de symbole: le code de l'intérieur B verra tous les symboles de A comme candidats possibles pour une recherche de symbole, qui peut confondre le code
- de l'avant-déclaration: Il n'y a aucun moyen de l'avant-déclarer Un::B sans donner l'intégralité de la déclaration d'Un
- Extensibilité: il est impossible d'ajouter une autre classe A::C à moins que vous ne soyez propriétaire d'un
- Code de verbosité: mettre les classes en classes rend seulement les en-têtes de plus. Vous pouvez toujours séparer ceci en plusieurs déclarations, mais il n'y a aucun moyen d'utiliser des alias, des importations ou des usings de type namespace.
comme conclusion, sauf exceptions (par exemple la classe nichée est une partie intime de la classe de nidification... Et même alors...), Je ne vois pas en classes imbriquées dans le code, comme les lacunes outweights par les magnitudes les avantages perçus.
de plus, cela sent comme une tentative maladroite de simuler l'Espace-nom sans utiliser les espaces-noms C++.
du côté pro, vous isolez ce code, et s'il est privé, vous le rendez inutilisable mais de la classe" outside"...
enum imbriqués
Pour: Tout.
Con: Rien.
Le fait est enum éléments vont polluer l'étendue globale:
// collision
enum Value { empty = 7, undefined, defined } ;
enum Glass { empty = 42, half, full } ;
// empty is from Value or Glass?
Ony en mettant chaque enum dans un espace/classe de nom différent vous permettra d'éviter cette collision:
namespace Value { enum type { empty = 7, undefined, defined } ; }
namespace Glass { enum type { empty = 42, half, full } ; }
// Value::type e = Value::empty ;
// Glass::type f = Glass::empty ;
noter que C++0x a défini la classe enum:
enum class Value { empty, undefined, defined } ;
enum class Glass { empty, half, full } ;
// Value e = Value::empty ;
// Glass f = Glass::empty ;
exactement pour ce genre de problèmes.
une con qui peut devenir une grosse affaire pour les grands projets est qu'il est impossible de faire une déclaration forward pour les classes imbriquées ou les énums.
si vous n'allez jamais utiliser la classe dépendante pour autre chose que travailler avec les implémentations de la classe indépendante, les classes imbriquées sont très bien, à mon avis.
c'est quand vous voulez utiliser la classe" interne " comme un objet à part entière que les choses peuvent commencer à devenir un peu maniaques et vous devez commencer à écrire des routines d'extraction/insertion. Pas une jolie situation.
il semble que vous devriez utiliser des espaces de noms au lieu de classes pour grouper comme des choses qui sont liées les unes aux autres de cette façon. Une arnaque que je pourrais voir dans faire des classes imbriquées est que vous finissez avec un fichier source vraiment grand qui pourrait être difficile à grok quand vous cherchez une section.
il n'y a pas de pour et de contre en soi de l'utilisation de classes publiques c++ imbriquées. Il y a seulement des faits. Ces faits sont prescrits par la norme C++. Si un fait sur les classes publiques c++ imbriquées est un pro ou un con dépend du problème particulier que vous essayez de résoudre. L'exemple que vous avez donné ne permet pas de juger si les classes imbriquées sont appropriées ou non.
un fait sur les classes imbriquées est, qu'ils ont un accès privilégié à tous les membres de la classe à laquelle ils appartiennent. C'est un con, si les classes imbriquées n'a pas besoin d'un tel accès. Mais si la classe imbriquée n'a pas besoin d'un tel accès, alors elle n'aurait pas dû être déclarée comme classe imbriquée. Il y a des situations où une classe a veut accorder un accès privilégié à certaines autres classes b . Il y a trois solutions à ce problème
- Faire B un ami A
- Faire B une classe imbriquée de Un
- faire les méthodes et les attributs, que B besoins, membres publics de A .
dans cette situation, il est #3 qui viole encapsulation, parce que A a le contrôle sur ses amis et sur ses classes imbriquées, mais pas sur les classes qui appellent son méthodes publiques ou accéder à ses attributs publics.
un autre fait sur les classes imbriquées est, qu'il est impossible d'ajouter une autre classe A::C comme une classe imbriquée de A sauf si vous êtes propriétaire de a . Cependant, cela est parfaitement raisonnable, parce que les classes imbriquées ont un accès privilégié. S'il était possible d'ajouter A::C comme classe imbriquée de A , alors A::C pourrait inciter Un dans l'octroi de l'accès à de l'information privilégiée; et que yould violer l'encapsulation. C'est essentiellement la même chose qu'avec la déclaration friend
: la déclaration friend
ne vous accorde aucun privilège spécial, que votre ami se cache des autres; elle permet à vos amis d'accéder à des informations que vous vous cachez de vos non-amis. En C++, appeler quelqu'un, un ami, c'est un acte altruiste, pas un égoïste. La même chose vaut pour permettre une catégorie à une classe imbriquée.
Som autres faits au sujet de imbriqués les classes publiques:
- la portée de A est candidate pour la recherche du symbole B : si vous ne voulez pas cela, faites B un ami de a au lieu d'une classe imbriquée. Cependant, il ya des cas où vous voulez exactement ce genre de recherche de symbole.
- A::B ne peut pas être avancé-déclaré: a et A::B sont étroitement couplés. Pouvoir utiliser A:: B sans savoir a ne ferait que Masquer ce fait.
Pour résumer cela: si l'outil ne correspond pas à vos besoins, ne blâme pas l'outil; vous blâmer pour l'utilisation de l'outil; d'autres pourraient avoir des problèmes différents, pour lesquels l'outil est parfait.
paercebal a dit tout ce que je dirais sur les énums emboîtés.
WRT classes imbriquées, mon cas d'utilisation commune et presque unique pour eux est quand j'ai une classe qui manipule un type spécifique de ressource, et j'ai besoin d'une classe de données qui représente quelque chose de spécifique à cette ressource. Dans votre cas, output_tray pourrait être un bon exemple, mais je n'utilise généralement pas les classes imbriquées si la classe va avoir des méthodes qui vont être appelées de l'extérieur du contenant de la classe, ou est plus que principalement une classe de données. En général, je ne niche pas non plus de classes de données à moins que la classe contain ne soit jamais référencée directement en dehors de la classe containing.
ainsi, par exemple, si j'avais une classe printer_manipulator, elle pourrait avoir une classe contenue pour les erreurs de manipulation d'imprimante, mais l'imprimante elle-même serait une classe non contenue.
Espérons que cette aide. :)
rappelez-vous que vous pouvez toujours promouvoir une classe imbriquée à un niveau supérieur plus tard, mais vous ne pouvez pas être en mesure de faire le contraire sans briser le code existant. Par conséquent, mon conseil serait d'en faire une classe imbriquée d'abord, et si elle commence à devenir un problème, en faire une classe de haut niveau dans la prochaine version.
pour moi, un grand inconvénient de l'avoir à l'extérieur, c'est qu'il fait partie de l'Espace-nom mondial. Si l'enum ou la classe apparentée ne s'applique qu'à la classe dans laquelle il est, alors c'est logique. Ainsi, dans le cas de l'imprimante, Tout ce qui inclut l'imprimante sera au courant de l'existence d'un accès complet à L'enum PRINTER_TYPE, où il n'a pas vraiment besoin de le savoir. Je ne peux pas dire que j'ai déjà utilisé un cours interne, mais pour un enum, ça semble plus logique de le garder à l'intérieur. Comme une autre affiche a fait remarquer, c'est aussi une bonne idée d'utiliser des namespaces pour grouper des éléments similaires, puisque colmater l'espace Nam global peut vraiment être une mauvaise chose. J'ai déjà travaillé sur des projets qui sont énormes et il faut 20 minutes pour dresser une liste complète automatique sur l'Espace-nom mondial. À mon avis, les énums imbriqués et les classes/structures avec noms sont probablement l'approche la plus propre.
je suis d'accord avec les messages préconisant l'intégration de votre enum dans une classe, mais il y a des cas où il est plus logique de ne pas le faire (mais s'il vous plaît, au moins mettez-le dans un espace de noms). Si plusieurs classes utilisent un enum défini dans une classe différente, alors ces classes dépendent directement de cette autre classe de béton (qui possède l'enum). Cela représente certainement un défaut de conception puisque cette classe sera responsable de cet enum ainsi que d'autres responsabilités.
donc, oui, intégrer l'enum dans une classe si un autre code n'utilise que l'enum pour interfacer directement avec cette classe de béton. Sinon, trouver un meilleur endroit pour garder l'enum tel qu'un espace de noms.
si vous mettez l'enum dans une classe ou un espace de noms, intellisense sera en mesure de vous guider quand vous essayez de vous souvenir des noms d'enum. Une petite chose à coup sûr, mais parfois les petites choses comptent.
Visual Studio 2008 ne semble pas être en mesure de fournir intellisense pour les classes imbriquées, donc je suis passé à L'idiome PIMPL dans la plupart des cas où j'avais l'habitude d'avoir une classe imbriquée. Je mets toujours enums soit dans la classe si elle est utilisée seulement par cette classe, soit en dehors de la classe dans le même espace de noms que la classe quand plus d'une classe utilise l'enum.
je peux voir un con pour les classes imbriquées, qu'on peut mieux utiliser la programmation générique.
si la petite classe est définie en dehors de la grande, vous pouvez faire de la grande classe un modèle de classe et utiliser n'importe quelle" petite " classe dont vous pourriez avoir besoin à l'avenir avec la grande classe.
la programmation générique est un outil puissant, et, IMHO, nous devrions le garder à l'esprit lors du développement de programmes extensibles. C'est étrange que personne n'ait mentionné ce point.
seul problème avec les classes imbriquées dans lesquelles je me suis cogné, c'est que C++ ne permet pas de se référer à l'objet de la classe enveloppante, dans les fonctions de classe imbriquées. Nous ne pouvons pas dire "enfermer:: ceci "
(mais il y a peut-être un moyen?)