Pregunta

Según tengo entendido, el idioma de pimpl existe solo porque C ++ te obliga a colocar a todos los miembros de la clase privada en el encabezado. Si el encabezado contuviera solo la interfaz pública, teóricamente, cualquier cambio en la implementación de la clase no habría requerido una recompilación para el resto del programa.

Lo que quiero saber es por qué C ++ no está diseñado para permitir tal comodidad. ¿Por qué exige en absoluto que las partes privadas de una clase se muestren abiertamente en el encabezado (sin juego de palabras)?

¿Fue útil?

Solución

Esto tiene que ver con el tamaño del objeto. El archivo h se usa, entre otras cosas, para determinar el tamaño del objeto. Si no se dan los miembros privados, entonces no sabría qué tan grande es un objeto como nuevo.

Sin embargo, puede simular su comportamiento deseado de la siguiente manera:

class MyClass
{
public:
   // public stuff

private:
#include "MyClassPrivate.h"
};

Esto no impone el comportamiento, pero saca las cosas privadas del archivo .h. En el lado negativo, esto agrega otro archivo para mantener. Además, en Visual Studio, el intellisense no funciona para los miembros privados, esto podría ser un plus o un menos.

Otros consejos

Creo que hay una confusión aquí. El problema no se trata de encabezados. Los encabezados no hacen nada (son solo formas de incluir fragmentos comunes de texto fuente entre varios archivos de código fuente).

El problema, por mucho que haya uno, es que las declaraciones de clase en C ++ tienen que definir todo, público y privado, que una instancia necesita tener para funcionar. (Lo mismo ocurre con Java, pero la forma en que funciona la referencia a clases compiladas externamente hace innecesario el uso de encabezados compartidos).

Es en la naturaleza de las tecnologías comunes orientadas a objetos (no solo la de C ++) que alguien necesita conocer la clase concreta que se usa y cómo usar su constructor para entregar una implementación, incluso si está usando solo el partes publicas. El dispositivo en (3, abajo) lo oculta. La práctica en (1, abajo) separa las preocupaciones, ya sea que lo haga (3) o no.

  1. Utilice clases abstractas que definan solo las partes públicas, principalmente métodos, y permita que la clase de implementación herede de esa clase abstracta. Entonces, usando la convención habitual para los encabezados, hay un abstract.hpp que se comparte. También hay una implementación.hpp que declara la clase heredada y que solo se pasa a los módulos que implementan métodos de implementación. El archivo implementación.hpp # incluirá " abstract.hpp " para usar en la declaración de clase que hace, de modo que haya un único punto de mantenimiento para la declaración de la interfaz abstraída.

  2. Ahora, si desea forzar la ocultación de la declaración de clase de implementación, debe tener alguna forma de solicitar la construcción de una instancia concreta sin poseer la declaración de clase específica y completa: no puede usar new y usted No se pueden usar instancias locales. (Sin embargo, puede eliminar). La introducción de funciones auxiliares (incluidos los métodos en otras clases que entregan referencias a instancias de clase) es el sustituto.

  3. Junto con o como parte del archivo de encabezado que se utiliza como definición compartida para la clase / interfaz abstracta, incluya firmas de funciones para funciones auxiliares externas. Estas funciones deben implementarse en módulos que forman parte de las implementaciones de clase específicas (para que vean la declaración de clase completa y puedan ejercer el constructor). La firma de la función auxiliar es probablemente muy parecida a la del constructor, pero como resultado devuelve una referencia de instancia (este proxy de constructor puede devolver un puntero NULO e incluso puede lanzar excepciones si le gusta ese tipo de cosas). La función auxiliar construye una instancia de implementación particular y la devuelve como referencia a una instancia de la clase abstracta.

Misión cumplida.

Ah, y la recompilación y la vinculación deberían funcionar de la manera deseada, evitando la recompilación de los módulos de llamada cuando solo cambia la implementación (ya que el módulo de llamada ya no hace ninguna asignación de almacenamiento para las implementaciones).

Todos ignoran el punto de la pregunta:

¿Por qué el desarrollador debe escribir el código PIMPL?

Para mí, la mejor respuesta que se me ocurre es que no tenemos una buena manera de expresar el código C ++ que le permite operar en él. Por ejemplo, tiempo de compilación (o preprocesador, o lo que sea) reflejo o un código DOM.

C ++ necesita urgentemente que uno o ambos estén disponibles para que un desarrollador haga metaprogramación.

Entonces podrías escribir algo como esto en tu MyClass.h pública:

#pragma pimpl(MyClass_private.hpp)

Y luego escribe tu propio generador de envoltura realmente trivial.

Alguien tendrá una respuesta mucho más detallada que yo, pero la respuesta rápida es doble: el compilador necesita conocer a todos los miembros de una estructura para determinar los requisitos de espacio de almacenamiento, y el compilador necesita saber el orden de esos miembros para generar compensaciones de una manera determinista.

El lenguaje ya es bastante complicado; Creo que un mecanismo para dividir las definiciones de datos estructurados en todo el código sería una especie de calamidad.

Normalmente, siempre he visto clases de políticas utilizadas para definir el comportamiento de implementación en un Pimpl-manera. Creo que hay algunos beneficios adicionales de usar un patrón de política: las implementaciones más fáciles de intercambiar, pueden combinar fácilmente múltiples implementaciones parciales en una sola unidad que le permite dividir el código de implementación en unidades funcionales, reutilizables, etc.

¿Puede ser porque el tamaño de la clase se requiere al pasar su instancia por valores, agregándola en otras clases, etc.?

Si C ++ no admitiera la semántica de valores, hubiera estado bien, pero lo hace.

Sí, pero ...

Necesita leer "Diseño y evolución de C ++" de Stroustrup libro. Hubiera inhibido la absorción de C ++.

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