¿Por qué necesitamos poner miembros privados en encabezados?

62

Las variables privadas son una forma de ocultar la complejidad y los detalles de implementación para el usuario de una clase. Esta es una característica bastante agradable. Pero no entiendo por qué en c ++ necesitamos ponerlos en el encabezado de una clase. Veo dos inconvenientes molestos para esto:

  • Abarrota el encabezado del usuario
  • Obliga a recompilar todas las bibliotecas de clientes cada vez que se modifican los componentes internos.

¿Hay alguna razón conceptual detrás de este requisito? ¿Es solo para facilitar el trabajo del compilador?

Simon Bergot
fuente
se puede declarar una estructura vacía en el encabezado, pero entonces sólo se puede utilizar punteros a una estructura tal cuando se usa (y no se puede asignar uno)
trinquete monstruo
3
@ratchetfreak: No, vacío ( struct foo{};) no está permitido, pero las declaraciones directas ( struct foo;) sí.
MSalters
@MSalters eso es lo que quise decir
fanático del trinquete
1
Permítanme agregar un inconveniente: * Escribir encabezados de funciones privadas en el archivo .h es una gran pérdida de tiempo. (olvidando las clases de amigos por un momento)
Jonny

Respuestas:

68

Esto se debe a que el compilador de C ++ debe conocer el tamaño real de la clase para asignar la cantidad correcta de memoria en la instanciación. Y el tamaño incluye todos los miembros, también los privados.

Una forma de evitar esto es usar el lenguaje Pimpl , explicado por Herb Sutter en su serie Guru of the Week # 24 y # 28 .

Actualizar

De hecho, esto (o más en general, la distinción del archivo de cabecera / fuente y #includes) es un obstáculo importante en C ++, heredado de C. En los días en que se creó C ++ C, todavía no había experiencia con el desarrollo de software a gran escala, donde esto comienza a causar problemas reales. Los diseñadores de los lenguajes más nuevos tuvieron en cuenta las lecciones aprendidas desde entonces, pero C ++ está sujeto a requisitos de compatibilidad con versiones anteriores, lo que hace que sea realmente difícil abordar un problema tan fundamental en el lenguaje.

Péter Török
fuente
¿No es este tipo de información solo contenido en la biblioteca de clases? ¿Se usa para vincular?
Simon Bergot
@ Simon, ¿qué quieres decir con "biblioteca de clases"?
Péter Török
Me refiero a la colección de archivos de objetos que contienen la definición de clase y los métodos
Simon Bergot
77
Cuando se creó C ++, AT&T / Bell Labs (el empleador de Stroustrups en ese momento) ciertamente tenía experiencia con el desarrollo de C a gran escala. Su software de cambio de teléfono 5ESS era en ese momento probablemente el programa C más grande del mundo. Las primeras ideas sobre OO ya son visibles en esa base de código, y Cfront imitó esas técnicas. Sin embargo, la noción de privatees más moderna.
MSalters
1
En C, simplemente pondría el asignador en una función de biblioteca; el cliente no podría asignar tal estructura en absoluto. Esto aumenta un poco la sobrecarga, pero hace que la migración del código a través de versiones sea trivial, por lo que a menudo vale la pena. Sin embargo, tiende a conducir a un estilo de código que es muy distinto del que se ve con C ++.
Donal Fellows
15

La definición de clase debe ser suficiente para que el compilador produzca un diseño idéntico en la memoria donde sea que haya utilizado un objeto de la clase. Por ejemplo, dado algo como:

class X { 
    int a;
public:
    int b;
};

El compilador normalmente tendrá aun desplazamiento 0 y bun desplazamiento 4. Si el compilador vio esto simplemente:

class X { 
public:
    int b;
};

"Pensaría" que bdebería estar en el desplazamiento 0 en lugar del desplazamiento 4. Cuando el código que usa esa definición asignada b, el código que usa la primera definición se vería amodificado, y viceversa.

La forma habitual de minimizar los efectos de realizar cambios en las partes privadas de la clase generalmente se llama modismo de pimpl (sobre el cual estoy seguro de que Google puede proporcionar una gran cantidad de información).

Jerry Coffin
fuente
1
Estoy preguntando sobre una decisión de diseño. Por supuesto, deberá colocar la declaración de miembro privado en algún lugar para que el idioma funcione. Pero, ¿por qué debería estar en el encabezado y no en un lugar más privado?
Simon Bergot
77
@Simon: El encabezado es todo lo que el compilador ve para decirle cómo se ve la clase / estructura. Ha habido discusiones sobre agregar algo como módulos a C ++, lo que ocultaría ese tipo de datos un poco más, pero hasta ahora no ha sido aprobado (aunque tampoco se ha eliminado por completo).
Jerry Coffin
3
Aún así, una regla trivial sería asignar tales miembros privados "definidos por .cpp" en último lugar. Eso significa que las compensaciones de los miembros públicos y privados "normales" no dependerían de ellos. En mi opinión, la verdadera razón es que no se puede heredar de esa clase, ya que la parte derivada debe seguir incluso a esos miembros privados.
MSalters
3

Probablemente hay varias razones. Si bien la mayoría de las otras clases no pueden acceder a los miembros privados, las clases de amigos aún pueden acceder a ellos. Entonces, al menos en este caso, pueden ser necesarios en el encabezado, para que la clase amiga pueda ver que existen.

La recompilación de archivos dependientes puede depender de su estructura de inclusión. La inclusión de los archivos .h en un archivo .cpp en lugar de otro encabezado puede en algunos casos evitar largas cadenas de recompilaciones.

Thorsten Müller
fuente