Pregunta

¿Cuáles son las ventajas y desventajas de usar clases y enumeraciones públicas anidadas de C ++? Por ejemplo, suponga que tiene una clase llamada printer y esta clase también almacena información en las bandejas de salida, podría tener:

class printer
{
public:
    std::string name_;

    enum TYPE
    {
        TYPE_LOCAL,
        TYPE_NETWORK,
    };

    class output_tray
    {
        ...
    };
    ...
};

printer prn;
printer::TYPE type;
printer::output_tray tray;

Alternativamente:

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;

Puedo ver los beneficios de anidar enums / clases privadas, pero cuando se trata de públicos, la oficina está dividida, parece ser más una opción de estilo.

Entonces, ¿cuál prefieres y por qué?

¿Fue útil?

Solución

Clases anidadas

Hay varios efectos secundarios en las clases anidadas dentro de las clases que generalmente considero fallas (si no son antipatrones puros).

Imaginemos el siguiente código:

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

   // etc.
} ;

O incluso:

class A
{
   public :
      class B ;

   // etc.
} ;

class A::B
{
   public :

   // etc.
} ;

Entonces:

  • Acceso privilegiado: A :: B tiene acceso privilegiado a todos los miembros de A (métodos, variables, símbolos, etc.), lo que debilita la encapsulación
  • El alcance de A es candidato para la búsqueda de símbolos: el código dentro de B verá todos los símbolos de A como posibles candidatos para una búsqueda de símbolos, lo que puede confundir el código
  • forward-statement: No hay forma de hacer un forward-declare A :: B sin dar una declaración completa de A
  • Extensibilidad: Es imposible agregar otra clase A :: C a menos que seas el propietario de A
  • Verbosidad del código: poner clases en clases solo hace que los encabezados sean más grandes. Aún puede separar esto en varias declaraciones, pero no hay manera de usar alias, importaciones o usos similares al espacio de nombres.

Como conclusión, a menos que haya excepciones (por ejemplo, la clase anidada es una parte íntima de la clase de anidación ... Y aún así ...), no veo ningún punto en las clases anidadas en el código normal, ya que las fallas superan en magnitud. Las ventajas percibidas.

Además, huele como un torpe intento de simular el espacio de nombres sin usar los espacios de nombres de C ++.

En el lado pro, aísla este código y, si es privado, lo hace inutilizable pero desde el " fuera " clase ...

enumerados anidados

Pros: Todo.

Con: Nada.

El hecho es que los elementos de enumeración contaminarán el alcance global:

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

// empty is from Value or Glass?

Solo al colocar cada enumeración en un espacio de nombres / clase diferente, podrás evitar esta colisión:

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 ;

Tenga en cuenta que C ++ 0x definió la enumeración de la clase:

enum class Value { empty, undefined, defined } ;
enum class Glass { empty, half, full } ;

// Value e = Value::empty ;
// Glass f = Glass::empty ;

exactamente para este tipo de problemas.

Otros consejos

Una estafa que puede convertirse en un gran problema para proyectos grandes es que es imposible hacer una declaración de reenvío para clases o enumerados anidados.

Si nunca vas a utilizar la clase dependiente para nada, pero trabajas con las implementaciones de la clase independiente, las clases anidadas están bien, en mi opinión.

Es cuando desea utilizar el " interno " clase como un objeto por derecho propio que las cosas pueden comenzar a ponerse un poco manky y tienes que empezar a escribir rutinas de extractor / insertador. No es una situación bonita.

Parece que deberías usar espacios de nombres en lugar de clases para agrupar cosas similares que están relacionadas entre sí de esta manera. Un problema que pude ver al hacer clases anidadas es que terminas con un archivo fuente realmente grande que podría ser difícil de asimilar cuando estás buscando una sección.

No hay ventajas ni desventajas de usar clases públicas anidadas de C ++. Sólo hay hechos. Esos hechos están obligados por el estándar de C ++. Si un hecho acerca de las clases públicas anidadas de C ++ es un profesional o una estafa, depende del problema particular que intenta resolver. El ejemplo que ha dado no permite un juicio acerca de si las clases anidadas son apropiadas o no.

Un hecho sobre las clases anidadas es que tienen acceso privilegiado a todos los miembros de la clase a la que pertenecen. Esta es una estafa, si las clases anidadas no necesitan tal acceso. Pero si la clase anidada no necesita dicho acceso, entonces no debería haberse declarado como una clase anidada. Hay situaciones en las que una clase A quiere otorgar acceso privilegiado a ciertas otras clases B . Hay tres soluciones para este problema

  1. Haz de B un amigo de A
  2. Haz de B una clase anidada de A
  3. Cree los métodos y atributos que B necesita, los miembros públicos de A .

En esta situación, es el # 3 que viola la encapsulación, porque A tiene control sobre sus amigos y sobre sus clases anidadas, pero no sobre clases que llaman a sus métodos públicos o acceden a sus atributos públicos.

Otro hecho sobre las clases anidadas es que es imposible agregar otra clase A :: C como una clase anidada de A a menos que sea propietario de A . Sin embargo, esto es perfectamente razonable, porque las clases anidadas tienen acceso privilegiado. Si fuera posible agregar A :: C como una clase anidada de A , entonces A :: C podría engañar a A en conceder acceso a información privilegiada; y que podrías violar la encapsulación. Básicamente, es lo mismo que con la declaración friend : la declaración friend no le otorga ningún privilegio especial, que su amigo está ocultando de los demás; les permite a tus amigos acceder a la información que estás ocultando de tus no amigos. En C ++, llamar amigo a alguien es un acto altruista, no egoísta. Lo mismo se aplica a permitir que una clase sea una clase anidada.

Otros datos sobre clases públicas anidadas:

  • El alcance de A es candidato para la búsqueda de símbolos de B : si no quieres esto, haz de B un amigo de A en lugar de una clase anidada Sin embargo, hay casos en los que desea exactamente este tipo de búsqueda de símbolos.
  • A::B no se puede declarar hacia adelante : A y A :: B están estrechamente acoplados. Poder usar A :: B sin saber A solo ocultaría este hecho.

Para resumir esto: si la herramienta no se ajusta a sus necesidades, no culpe a la herramienta; culparte por usar la herramienta; Otros pueden tener diferentes problemas, para los cuales la herramienta es perfecta.

paercebal dijo todo lo que diría sobre enumeraciones anidadas.

Clases anidadas de WRT, mi caso de uso común y casi único es cuando tengo una clase que manipula un tipo específico de recurso, y necesito una clase de datos que represente algo específico de ese recurso. En su caso, output_tray puede ser un buen ejemplo, pero generalmente no uso clases anidadas si la clase tendrá algún método al que se llamará desde fuera de la clase que lo contiene, o es más que una clase de datos. Por lo general, tampoco anidado clases de datos a menos que la clase contenida no sea referenciada directamente fuera de la clase contenedora.

Entonces, por ejemplo, si tuviera una clase printer_manipulator, podría tener una clase contenida para los errores de manipulación de la impresora, pero la impresora en sí misma sería una clase no contenida.

Espero que esto ayude. :)

Recuerde que siempre puede promover una clase anidada a una de nivel superior más adelante, pero es posible que no pueda hacer lo contrario sin romper el código existente. Por lo tanto, mi consejo sería que primero sea una clase anidada, y si comienza a convertirse en un problema, conviértalo en una clase de nivel superior en la próxima versión.

Para mí, una gran desventaja de tenerlo fuera es que se convierte en parte del espacio de nombres global. Si la enumeración o la clase relacionada solo se aplica realmente a la clase en la que está, entonces tiene sentido. Por lo tanto, en el caso de la impresora, todo lo que incluye la impresora sabrá cómo tener acceso completo a la enumeración PRINTER_TYPE, donde realmente no es necesario que la conozca. No puedo decir que alguna vez haya usado una clase interna, pero para una enumeración, parece más lógico mantenerla dentro. Como ha señalado otro póster, también es una buena idea usar espacios de nombres para agrupar elementos similares, ya que obstruir el espacio de nombres global puede ser realmente malo. Anteriormente, he trabajado en proyectos que son masivos y solo para acceder a una lista completa automática en el espacio de nombres global toma 20 minutos. En mi opinión, las enumeraciones anidadas y las clases / estructuras con espacios de nombre son probablemente el enfoque más limpio.

Estoy de acuerdo con las publicaciones que abogan por incrustar tu enumeración en una clase, pero hay casos en los que tiene más sentido no hacerlo (pero, por favor, ponlo en un espacio de nombres). Si varias clases utilizan una enumeración definida dentro de una clase diferente, entonces esas clases dependen directamente de esa otra clase concreta (que posee la enumeración). Eso seguramente representa una falla de diseño, ya que esa clase será responsable de esa enumeración y de otras responsabilidades.

Entonces, sí, incrusta la enumeración en una clase si otro código solo usa esa enumeración para interactuar directamente con esa clase concreta. De lo contrario, busque un lugar mejor para guardar la enumeración, como un espacio de nombres.

Si coloca la enumeración en una clase o en un espacio de nombres, intellisense podrá orientarlo cuando intente recordar los nombres de la enumeración. Una pequeña cosa con seguridad, pero a veces las cosas pequeñas son importantes.

Visual Studio 2008 no parece ser capaz de proporcionar inteligencia para las clases anidadas, por lo que he cambiado al lenguaje PIMPL en la mayoría de los casos donde solía tener una clase anidada. Siempre pongo enums en la clase si solo la usa esa clase, o fuera de la clase en el mismo espacio de nombres que la clase cuando más de una clase usa la enumeración.

Puedo ver una estafa para las clases anidadas, que se puede usar mejor la programación genérica.

Si la clase pequeña se define fuera de la grande, puedes hacer que la clase grande sea una plantilla de clase y usar cualquier " pequeña " clase que puede necesitar en el futuro con la clase grande.

La programación genérica es una herramienta poderosa y, en mi humilde opinión, debemos tenerla en cuenta al desarrollar programas extensibles. Es extraño que nadie haya mencionado este punto.

El único problema con las clases anidadas con las que me topé todavía era que C ++ no nos permite referirnos al objeto de la clase adjunta, en las funciones de clase anidadas. No podemos decir " Enclosing :: this "

(¿Pero tal vez hay una manera?)

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top