resueltos - programacion orientada a objetos c++ ejemplos




¿Por qué uno usaría clases anidadas en C++? (3)

Las clases anidadas son como las clases normales, pero:

  • tienen restricción de acceso adicional (como lo hacen todas las definiciones dentro de una definición de clase),
  • no contaminan el espacio de nombres dado , por ejemplo, el espacio de nombres global. Si sientes que la clase B está tan profundamente conectada a la clase A, pero los objetos de A y B no están necesariamente relacionados, entonces puede que desees que la clase B sea accesible solo mediante el alcance de la clase A (se la llamaría A ::Clase).

Algunos ejemplos:

Clasificar públicamente la clase para ponerla en un ámbito de clase relevante

Supongamos que quiere tener una clase SomeSpecificCollection que agregue objetos de la clase Element . Entonces puede:

  1. declara dos clases: SomeSpecificCollection y Element - bad, porque el nombre "Element" es lo suficientemente general como para causar un posible choque de nombre

  2. introduzca un espacio de nombres someSpecificCollection y declare las clases someSpecificCollection::Collection y someSpecificCollection::Element . No hay riesgo de conflicto de nombres, pero ¿puede ser más detallado?

  3. declare dos clases globales SomeSpecificCollection y SomeSpecificCollectionElement , que tiene inconvenientes menores, pero probablemente esté bien.

  4. declara la clase global SomeSpecificCollection y el Element clase como clase anidada. Entonces:

    • no se arriesga a ningún conflicto de nombres ya que Element no está en el espacio de nombres global,
    • en la implementación de SomeSpecificCollection se refiere a solo Element , y en cualquier otro lugar como SomeSpecificCollection::Element , que se ve + - lo mismo que 3., pero más claro
    • se vuelve simple que es "un elemento de una colección específica", no "un elemento específico de una colección"
    • es visible que SomeSpecificCollection también es una clase.

En mi opinión, la última variante es definitivamente la más intuitiva y, por lo tanto, el mejor diseño.

Permítanme enfatizar: no es una gran diferencia hacer dos clases globales con nombres más detallados. Es solo un pequeño detalle, pero en el que hace que el código sea más claro.

Presentamos otro alcance dentro de un alcance de clase

Esto es especialmente útil para introducir typedefs o enums. Voy a publicar un ejemplo de código aquí:

class Product {
public:
    enum ProductType {
        FANCY, AWESOME, USEFUL
    };
    enum ProductBoxType {
        BOX, BAG, CRATE
    };
    Product(ProductType t, ProductBoxType b, String name);

    // the rest of the class: fields, methods
};

Entonces uno llamará:

Product p(Product::FANCY, Product::BOX);

Pero al ver las propuestas de finalización de código para Product:: , a menudo se mostrarán todos los valores de enumeración posibles (BOX, FANCY, CRATE) y es fácil cometer un error aquí (las enums fuertemente tipadas de C ++ 0x resuelven eso, pero no importa).

Pero si introduce un alcance adicional para las enumeraciones que usan clases anidadas, las cosas podrían verse así:

class Product {
public:
    struct ProductType {
        enum Enum { FANCY, AWESOME, USEFUL };
    };
    struct ProductBoxType {
        enum Enum { BOX, BAG, CRATE };
    };
    Product(ProductType::Enum t, ProductBoxType::Enum b, String name);

    // the rest of the class: fields, methods
};

Entonces la llamada se ve así:

Product p(Product::ProductType::FANCY, Product::ProductBoxType::BOX);

Luego, al escribir Product::ProductType:: en un IDE, solo obtendrá las enumeraciones del alcance deseado sugerido. Esto también reduce el riesgo de cometer un error.

Por supuesto, esto puede no ser necesario para clases pequeñas, pero si uno tiene muchas enumeraciones, entonces hace las cosas más fáciles para los programadores clientes.

De la misma manera, podría "organizar" un gran grupo de typedefs en una plantilla, si alguna vez tuvo la necesidad de hacerlo. Es un patrón útil a veces.

El modismo PIMPL

El PIMPL (abreviatura de Pointer to IMPLementation) es una expresión idiomática útil para eliminar los detalles de implementación de una clase del encabezado. Esto reduce la necesidad de recompilar clases dependiendo del encabezado de la clase siempre que la parte de "implementación" del encabezado cambie.

Por lo general, se implementa utilizando una clase anidada:

Xh:

class X {
public:
    X();
    virtual ~X();
    void publicInterface();
    void publicInterface2();
private:
    struct Impl;
    std::unique_ptr<Impl> impl;
}

X.cpp:

#include "X.h"
#include <windows.h>

struct X::Impl {
    HWND hWnd; // this field is a part of the class, but no need to include windows.h in header
    // all private fields, methods go here

    void privateMethod(HWND wnd);
    void privateMethod();
};

X::X() : impl(new Impl()) {
    // ...
}

// and the rest of definitions go here

Esto es particularmente útil si la definición de clase completa necesita la definición de tipos de alguna biblioteca externa que tenga un archivo de encabezado pesado o simplemente feo (tome WinAPI). Si usa PIMPL, puede adjuntar cualquier funcionalidad específica de .cpp solo en .cpp y nunca incluirlo en .h .

¿Puede alguien señalarme algunos recursos útiles para comprender y usar clases anidadas? Tengo algunos materiales como Principios de programación y cosas como este IBM Knowledge Center - Clases anidadas

Pero todavía tengo problemas para entender su propósito. ¿Alguien podría ayudarme por favor?


Las clases anidadas son geniales para ocultar los detalles de implementación

Lista:

class List
{
    public:
        List(): head(NULL), tail(NULL) {}
    private:
        class Node
        {
              public:
                  int   data;
                  Node* next;
                  Node* prev;
        };
    private:
        Node*     head;
        Node*     tail;
};

Aquí no quiero exponer Node porque otras personas pueden decidir usar la clase y eso me impediría actualizar mi clase, ya que todo lo expuesto es parte de la API pública y debe mantenerse para siempre . Al hacer que la clase sea privada, no solo oculto la implementación. También digo que esto es mío y que puedo cambiarlo en cualquier momento para que no pueda usarlo.

Mire std::list o std::map , todos ellos contienen clases ocultas (¿o no?). El punto es que pueden o no, pero debido a que la implementación es privada y oculta, los constructores del STL pudieron actualizar el código sin afectar la forma en que usaste el código, o dejando un montón de equipaje viejo en el STL porque necesitan para mantener la compatibilidad hacia atrás con algún tonto que decidió que querían usar la clase Node que estaba oculta dentro de la list .








inner-classes