Pregunta

Esta pregunta ya tiene una respuesta aquí:

Antecedentes:

El PIMPL Idiom (Puntero a la Aplicación) es una técnica para la implementación de la clandestinidad en la que una clase pública envuelve una clase o estructura que no puede ser visto fuera de la biblioteca de la clase pública es parte de.

Esto oculta la implementación interna de los detalles y los datos del usuario de la biblioteca.

Cuando la aplicación de este modismo ¿por qué habría de colocar los métodos públicos en el pimpl de clase y no de la clase pública ya que el público método de las clases de implementaciones se compilarán en la biblioteca y el usuario sólo tiene el archivo de encabezado?

Para ilustrar, este código pone el Purr() aplicación en la impl clase y se ajusta bien.

¿Por qué no aplicar el Ronroneo directamente en la clase pública?

// header file:
class Cat {
    private:
        class CatImpl;  // Not defined here
        CatImpl *cat_;  // Handle

    public:
        Cat();            // Constructor
        ~Cat();           // Destructor
        // Other operations...
        Purr();
};


// CPP file:
#include "cat.h"

class Cat::CatImpl {
    Purr();
...     // The actual implementation can be anything
};

Cat::Cat() {
    cat_ = new CatImpl;
}

Cat::~Cat() {
    delete cat_;
}

Cat::Purr(){ cat_->Purr(); }
CatImpl::Purr(){
   printf("purrrrrr");
}
¿Fue útil?

Solución

  • Porque quieres Purr() ser capaz de utilizar los miembros privados de CatImpl. Cat::Purr() no se permite el acceso sin friend de la declaración.
  • Debido a que usted no mezclar responsabilidades:una clase implementa una clase hacia delante.

Otros consejos

Creo que la mayoría de las personas se refieren a esto como el Mango Cuerpo idioma.Ver a James Coplien del libro Avanzado de Programación en C++ Estilos y expresiones Idiomáticas (Link de Amazon).Es también conocido como el El Gato De Cheshire porque de Lewis Caroll carácter que se desvanece hasta que sólo la sonrisa sigue siendo.

El ejemplo de código debe ser distribuido a través de dos conjuntos de archivos de origen.A continuación, sólo Gato.h es el archivo que se incluye con el producto.

CatImpl.h es incluido por Cat.cpp y CatImpl.cpp contiene la aplicación para CatImpl::Purr().Este no será visible para el público, a través de su producto.

Básicamente la idea es ocultar tanto como sea posible de la aplicación de las miradas indiscretas.Esto es muy útil cuando usted tiene un producto comercial que se envía como una serie de bibliotecas que se accede a través de una API que el cliente código es compilado en contra y vinculado.

Lo hicimos con la reescritura de IONAs Orbix 3.3 producto en el año 2000.

Como se ha mencionado por otros, usando su técnica completamente desacopla la implementación de la interfaz del objeto.Entonces usted no tiene que volver a compilar todo lo que utiliza Gato si sólo desea cambiar la implementación de Purr().

Esta técnica es utilizada en una metodología llamada diseño por contrato.

Por lo que vale la pena, se separa de la implementación de la interfaz.Esto no suele ser muy importante en pequeño tamaño de los proyectos.Pero, en los grandes proyectos y las bibliotecas, que puede ser utilizado para reducir los tiempos de construcción de manera significativa.

Considerar que la aplicación de Cat puede incluir muchos de los encabezados, puede implicar la plantilla de meta-programación que toma tiempo para compilar en su propio.¿Por qué debería un usuario, que sólo quiere usar el Cat tiene que incluir a todos los que?Por lo tanto, todos los archivos necesarios están ocultos mediante la pimpl lenguaje (de ahí el avance de la declaración de CatImpl), y el uso de la interfaz de no obligar al usuario a que los incluyen.

Estoy desarrollando una biblioteca de optimización no lineal (leer "un montón de desagradables matemáticas"), que se implementa en las plantillas, por lo que la mayoría del código es en los encabezados.Se tarda unos cinco minutos para compilar (en un decente multi-core CPU), y sólo el análisis de las cabeceras en un vacío de otra manera .cpp toma alrededor de un minuto.Así que cualquier persona que utilice la biblioteca tiene que esperar un par de minutos cada vez que compilar su código, lo que hace que el desarrollo tedioso.Sin embargo, al ocultar la implementación y los encabezados, uno sólo incluye una interfaz simple de archivo, que compila al instante.

No necesariamente tiene nada que ver con la protección de la implementación de ser copiado por otras empresas - que no probablemente a suceder de todos modos, a menos que el funcionamiento interno de su algoritmo puede ser deducido a partir de las definiciones de las variables miembro (si es así, es probable que no sea muy complicado y no vale la pena proteger, en primer lugar).

Si la clase se utiliza el pimpl lenguaje, puede evitar cambiar el archivo de encabezado en la clase pública.

Esto le permite agregar o quitar métodos para la pimpl clase, sin modificar la clase externos del archivo de encabezado.Usted puede también agregar/quitar #incluye a la pimpl demasiado.

Cuando cambiar el archivo de encabezado de la clase de archivo, usted tiene que volver a compilar todo lo que #incluye (y si alguno de esos son los archivos de cabecera, tiene que volver a compilar todo lo que #los incluye a ellos, y así sucesivamente)

Normalmente, la única referencia a Pimpl clase en el encabezado por el Titular de la clase (Gato, en este caso) sería una declaración forward, como has hecho aquí, ya que pueden reducir considerablemente las dependencias.

Por ejemplo, si su Pimpl clase tiene ComplicatedClass como un miembro (y no sólo de un puntero o referencia a ella), entonces usted tendría que tener ComplicatedClass totalmente definido antes de su uso.En la práctica, esto significa incluyendo "ComplicatedClass.h" (que también indirectamente incluir cualquier cosa ComplicatedClass depende).Esto puede conducir a un único encabezado llenar tirando en un montón y un montón de cosas, lo cual es malo para la gestión de sus dependencias (y sus tiempos de compilación).

Cuando se utiliza el pimpl idion, usted sólo necesita para #incluir el material utilizado en la interfaz pública de su Propietario tipo (que sería Cat aquí).Que hace las cosas mejor para las personas a utilizar su biblioteca, y significa que usted no necesita preocuparse acerca de las personas, dependiendo de algunos de la parte interior de su biblioteca -, ya sea por error, o porque quieren hacer algo que usted no permitir así que #define público privado antes de incluir sus archivos.

Si es una clase simple, por lo general hay ninguna razón para utilizar un Pimpl, pero para tiempos cuando los tipos son muy grandes, puede ser una gran ayuda (especialmente en evitar largos tiempos de construcción)

Bueno, yo no lo uso.Tengo una mejor alternativa:

foo.h:

class Foo {
public:
    virtual ~Foo() { }
    virtual void someMethod() = 0;

    // This "replaces" the constructor
    static Foo *create();
}

foo.cpp:

namespace {
    class FooImpl: virtual public Foo {

    public:
        void someMethod() { 
            //....
        }     
    };
}

Foo *Foo::create() {
    return new FooImpl;
}

¿Este patrón tiene un nombre?

Como también Python y Java programador, me gusta mucho más que el pImpl idioma.

Utilizamos PIMPL lenguaje con el fin de emular la programación orientada al aspecto donde el pre, post y error aspectos son llamados antes y después de la ejecución de una función miembro.

struct Omg{
   void purr(){ cout<< "purr\n"; }
};

struct Lol{
  Omg* omg;
  /*...*/
  void purr(){ try{ pre(); omg-> purr(); post(); }catch(...){ error(); } }
};

También utilizamos el puntero a la clase base para compartir diferentes aspectos entre muchas clases.

El inconveniente de este enfoque es que el usuario de la biblioteca ha de tener en cuenta todos los aspectos que se van a ejecutar, pero sólo ve a su clase.Se requiere de exploración de la documentación de cualquier efectos secundarios.

La colocación de la llamada a la impl->Purr dentro del archivo cpp significa que en el futuro se podría hacer algo completamente diferente sin tener que cambiar el archivo de encabezado.Tal vez el próximo año se descubre que un método auxiliar que podría haber llamado en su lugar y así se puede cambiar el código para llamar directamente y no uso impl->Ronroneo en todo.(Sí, se podría lograr lo mismo mediante la actualización de la real impl::Purr método, pero en caso de que usted está atascado con un extra de llamada de función que consigue nada, pero llamar a la siguiente función en cada turno)

También significa el encabezado sólo tiene definiciones y no tiene ninguna aplicación que hace que para una separación más clara, que es el punto entero de la frase.

He implementado mi primer pimpl clase durante el último par de días.Yo lo he utilizado para eliminar los problemas que estaba teniendo incluyendo winsock2.h en Borland Builder.Parecía ser atornillado estructura de alineación y desde que me había socket cosas en la clase de datos privados, los problemas se extiende a cualquier archivo cpp que incluye el encabezado.

Mediante el uso de pimpl, winsock2.h fue incluido en sólo un archivo cpp donde yo podría poner una tapa en el problema y no te preocupes que iba a volver para vengarse de mí.

Para responder a la pregunta original, la ventaja que he encontrado en el desvío de las llamadas a la pimpl de la clase fue que el pimpl de la clase es la misma que la de la clase original habría sido antes de pimpl gustaría que, además de las implementaciones no son repartidas en 2 clases en alguna extraña moda.Es mucho más clara para implementar el público se remite simplemente a la pimpl clase.

Al igual que el Señor Nodet dijo, una clase, una responsabilidad.

No sé si esto es una diferencia que vale la pena mencionar, pero...

Sería posible tener la aplicación en su propio espacio de nombres y tienen un público contenedor / biblioteca de espacio de nombres para el código que ve el usuario:

catlib::Cat::Purr(){ cat_->Purr(); }
cat::Cat::Purr(){
   printf("purrrrrr");
}

De este modo, todos del código de la biblioteca puede hacer uso de el gato de espacio de nombres y la necesidad de exponer una clase para el usuario surge un contenedor podría ser creado en el catlib espacio de nombres.

Me parece significativo que, a pesar de lo conocido que es el pimpl modismo es, yo no lo veo cultivo muy a menudo en la vida real (por ejemplo,en los proyectos de código abierto).

A menudo me pregunto si los "beneficios" son exagerados;sí, usted puede hacer algunos de los detalles de la implementación aún más oculto, y sí, usted puede cambiar de aplicación sin cambiar el encabezado, pero no es obvio que se trata de grandes ventajas en la realidad.

Es decir, no está claro que hay alguna necesidad de su aplicación para ser que bien escondido, y tal vez es muy raro que la gente realmente hacer el cambio de la implementación;tan pronto como usted necesita añadir nuevos métodos, digamos, usted necesita para cambiar el encabezado de todos modos.

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