Avantages et inconvénients de l'utilisation de classes et d'énumérations imbriquées C ++?

StackOverflow https://stackoverflow.com/questions/216748

  •  03-07-2019
  •  | 
  •  

Question

Quels sont les avantages et les inconvénients de l'utilisation de classes et d'énumérations C ++ publiques imbriquées? Par exemple, supposons que vous ayez une classe appelée printer et que cette classe stocke également des informations sur les bacs 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'imbrication d'énums / de classes privées, mais lorsqu'il s'agit d'environnements publics, le bureau est divisé - cela semble être davantage un choix de style.

Alors, que préférez-vous et pourquoi?

Était-ce utile?

La solution

Classes imbriquées

Il existe plusieurs effets secondaires sur les classes imbriquées dans des classes que je considère généralement comme des défauts (si ce n’est pas de simples antipatterns).

Imaginons le code suivant:

class A
{
   public :
      class B { /* etc. */ } ;

   // etc.
} ;

Ou même:

class A
{
   public :
      class B ;

   // etc.
} ;

class A::B
{
   public :

   // etc.
} ;

Donc:

  • Accès privilégié: A :: B dispose d’un accès privilégié à tous les membres de A (méthodes, variables, symboles, etc.), ce qui affaiblit l’encapsulation
  • La portée de A est candidate à la recherche de symbole: le code à l'intérieur de B verra tous les symboles de A comme candidats possibles pour une recherche de symbole, ce qui peut confondre le code
  • déclaration en aval: Il n’existe aucun moyen de déclarer en aval A :: B sans donner une déclaration complète de A
  • Extensibilité: Il est impossible d'ajouter une autre classe A :: C à moins d'être propriétaire de A
  • Code de verbosité: l'insertion de classes dans des classes agrandit uniquement les en-têtes. Vous pouvez toujours séparer cela en plusieurs déclarations, mais il n’ya aucun moyen d’utiliser des alias, des imports ou des usings similaires à des espaces de noms.

En conclusion, à moins d'exceptions (par exemple, la classe imbriquée est une partie intime de la classe d'imbrication ... Et même dans ce cas ...), je ne vois pas l'intérêt des classes imbriquées dans le code normal, car les défauts sont supérieurs à la magnitude. les avantages perçus.

En outre, cela ressemble à une tentative maladroite de simuler un espace de noms sans utiliser les espaces de noms C ++.

Du côté des pros, vous isolez ce code et, s’il est privé, vous le rendez inutilisable, mais à partir du champ "extérieur". classe ...

Enums imbriqués

Avantages: tout.

Con: rien.

Le fait est que les éléments enum vont polluer la portée globale:

// collision
enum Value { empty = 7, undefined, defined } ;
enum Glass { empty = 42, half, full } ;

// empty is from Value or Glass?

En mettant chaque énumération dans un espace de nom / classe différent, vous éviterez 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 ;

Notez que C ++ 0x a défini l'énumération de classe:

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.

Autres conseils

Un inconvénient qui peut devenir un gros problème pour les grands projets est qu’il est impossible de faire une déclaration directe pour les classes ou les énumérations imbriquées.

À mon avis, si vous n'utilisez jamais la classe dépendante pour autre chose que de travailler avec les implémentations de la classe indépendante, les classes imbriquées conviennent parfaitement.

C’est lorsque vous souhaitez utiliser le paramètre "interne". classe comme un objet en soi que les choses peuvent commencer à devenir un peu manky et que vous devez commencer à écrire des routines extracteur / introducteur Pas une jolie situation.

Il semble que vous devriez utiliser des espaces de noms plutôt que des classes pour regrouper des éléments similaires les uns aux autres. L'un des inconvénients que j'ai pu constater en faisant des classes imbriquées est que vous vous retrouvez avec un très gros fichier source qui pourrait être difficile à assimiler lorsque vous recherchez une section.

Il n’existe aucun avantage ni aucun inconvénient à utiliser des classes C ++ publiques imbriquées. Il n'y a que des faits. Ces faits sont prescrits par le standard C ++. Qu'un fait sur les classes C ++ publiques imbriquées soit un avantage ou un inconvénient dépend du problème particulier que vous essayez de résoudre. L'exemple que vous avez donné ne permet pas de déterminer si les classes imbriquées sont appropriées ou non.

Un fait concernant les classes imbriquées est qu’elles ont un accès privilégié à tous les membres de la classe à laquelle elles appartiennent. C'est un inconvénient, si les classes imbriquées n'ont pas besoin d'un tel accès. Mais si la classe imbriquée n'a pas besoin d'un tel accès, elle n'aurait pas dû être déclarée en tant que classe imbriquée. Il existe des situations dans lesquelles une classe A souhaite accorder un accès privilégié à certaines autres classes B . Il y a trois solutions à ce problème

  1. Faites de B un ami de A
  2. Faites de B une classe imbriquée de A
  3. Définissez les méthodes et les attributs dont B a besoin, ainsi que les membres publics de A .

Dans cette situation, c'est le numéro 3 qui viole l'encapsulation, car A a le contrôle sur ses amis et sur ses classes imbriquées, mais pas sur les classes qui appellent ses méthodes publiques ou accèdent à ses attributs publics. / p>

Un autre fait concernant les classes imbriquées est qu'il est impossible d'ajouter une autre classe A :: C en tant que classe imbriquée de A à moins d'être propriétaire de . A . Cependant, cela est parfaitement raisonnable, car les classes imbriquées ont un accès privilégié. S'il était possible d'ajouter A :: C en tant que classe imbriquée de A , A :: C pourrait alors piéger A à donner accès à des informations privilégiées; et que vous violeriez l’encapsulation. C'est fondamentalement la même chose que pour la déclaration ami : la déclaration ami ne vous accorde aucun privilège spécial, que votre ami se cache des autres; il permet à vos amis d'accéder aux informations que vous cachez à vos non-amis. En C ++, appeler quelqu'un comme ami est un acte altruiste et non égoïste. Il en va de même pour permettre à une classe d'être une classe imbriquée.

Quelques autres faits sur les classes publiques imbriquées:

  • La portée de A est candidate à la recherche de symbole de B : si vous ne le souhaitez pas, faites de B un ami de A au lieu de une classe imbriquée. Cependant, il existe des cas où vous voulez exactement ce genre de recherche de symbole.
  • A :: B ne peut pas être déclaré en aval : A et A :: B sont étroitement couplés. Pouvoir utiliser A :: B sans savoir A ne ferait que masquer ce fait.

Pour résumer, si l’outil ne répond pas à vos besoins, ne le blâmez pas; vous blâmer d'avoir utilisé l'outil; d'autres peuvent avoir des problèmes différents, pour lesquels l'outil est parfait.

paercebal a dit tout ce que je pourrais dire sur les énumérations imbriquées.

Les classes imbriquées WRT, mon cas d'utilisation commun et presque unique est lorsqu'elles ont une classe manipulant un type spécifique de ressource et que j'ai besoin d'une classe de données représentant quelque chose de spécifique à cette ressource. Dans votre cas, output_tray est peut-être un bon exemple, mais je n'utilise généralement pas de classes imbriquées si la classe doit avoir des méthodes appelées de l'extérieur de la classe contenante ou s'il s'agit davantage d'une classe de données. De manière générale, je n'emboîte pas non plus les classes de données, à moins que la classe contenue ne soit jamais directement référencée en dehors de la classe contenante.

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.

J'espère que ça aide. :)

N'oubliez pas que vous pouvez toujours promouvoir une classe imbriquée en classe supérieure plus tard, mais que vous ne pourrez peut-être pas faire l'inverse sans détruire le code existant. Par conséquent, mon conseil serait d’en faire une classe imbriquée d’abord, et si cela commence à poser problème, faites-en une classe de niveau supérieur dans la prochaine version.

Pour moi, l’un des gros inconvénients de l’extérieur est qu’il devient une partie de l’espace de noms global. Si l'énumération ou la classe associée ne s'applique vraiment qu'à la classe dans laquelle elle se trouve, alors cela a du sens. Ainsi, dans le cas de l’imprimante, tout ce qui inclut l’imprimante sait qu’il doit avoir un accès complet à l’énumération PRINTER_TYPE, où elle n’a pas vraiment besoin de le savoir. Je ne peux pas dire que j'ai déjà utilisé une classe interne, mais pour une énumération, cela semble plus logique de la garder à l'intérieur. Comme l'a souligné une autre affiche, il est également judicieux d'utiliser des espaces de noms pour regrouper des éléments similaires, car l'encrassement de l'espace de noms global peut être une mauvaise chose. J'ai déjà travaillé sur des projets de grande envergure et le simple fait de mettre en place une liste complète automatique sur l'espace de noms global prend 20 minutes. À mon avis, les énumérations imbriquées et les classes / structures namespaced sont probablement l’approche la plus propre.

Je suis d’accord avec les publications qui préconisent l’incorporation de votre enum dans une classe, mais il est parfois plus logique de ne pas le faire (mais veuillez au moins le placer dans un espace de noms). Si plusieurs classes utilisent une énumération définie dans une classe différente, ces classes dépendent directement de cette autre classe concrète (qui possède l'énum). Cela représente sûrement un défaut de conception puisque cette classe sera responsable de cette énumération ainsi que d’autres responsabilités.

Alors, oui, incorporer l'énum dans une classe si un autre code utilise uniquement cette énum pour se connecter directement à cette classe concrète. Sinon, trouvez un meilleur endroit pour conserver l'énumération telle qu'un espace de noms.

Si vous placez l'énum dans une classe ou un espace de noms, intellisense sera en mesure de vous guider lorsque vous essayez de vous rappeler les noms d'énum. Une petite chose certes, mais parfois les petites choses importent.

Visual Studio 2008 ne semble pas être en mesure de fournir intellisense aux classes imbriquées. Je suis donc passé à l'idiome PIMPL dans la plupart des cas où j'avais une classe imbriquée. Je mets toujours des énumérations dans la classe si elle est utilisée uniquement par cette classe, ou en dehors de la classe dans le même espace de nom que la classe lorsque plusieurs classes utilisent l’énumération.

Je peux voir un con pour les classes imbriquées, que l'on pourrait mieux utiliser la programmation générique.

Si la petite classe est définie en dehors de la grande, vous pouvez en faire un modèle de classe et utiliser n'importe quel "petit". classe dont vous aurez peut-être besoin à l’avenir avec la grande classe.

La programmation générique est un outil puissant et, à mon humble avis, nous devons en tenir compte lors du développement de programmes extensibles. Étrange, que personne n’a mentionné ce point.

Le seul problème avec les classes imbriquées dans lequel je me suis heurté jusqu'à présent était que C ++ ne nous permettait pas de faire référence à l'objet de la classe englobante, dans les fonctions de classe imbriquées. Nous ne pouvons pas dire "Enclosing :: this"

(Mais peut-être qu'il y a un moyen?)

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top