¿Por qué ponemos funciones miembro privadas en los encabezados?

14

La respuesta a la razón por la que colocamos las variables miembro privadas en los encabezados de C ++ es que el tamaño de la clase debe conocerse en los puntos donde se declaran las instancias para que el compilador pueda generar código que se mueva adecuadamente en la pila. ¿Por qué necesitamos poner privado? miembros en encabezados?

¿Pero hay alguna razón para declarar funciones privadas en la definición de clase?

La alternativa sería esencialmente el lenguaje pimpl pero sin la indirección superflua.

¿Es esta característica del lenguaje más que un error histórico?

    
pregunta Praxeolitic 15.05.2014 - 16:48

6 respuestas

10

Las funciones miembro privadas pueden ser virtual , y en implementaciones comunes de C ++ (que usan una vtable) el orden específico y el número de funciones virtuales deben ser conocidos por todos los clientes de la clase. Esto se aplica incluso si una o más de las funciones miembro virtuales es private .

Puede parecer que esto es como "poner el carro antes que el caballo", porque las opciones de implementación del compilador no deberían afectar la especificación del idioma. Sin embargo, en realidad, el lenguaje C ++ se desarrolló al mismo tiempo que una implementación funcional ( Cfront ), que utilizaba vtables.

    
respondido por el Greg Hewgill 15.05.2014 - 22:36
10

Si permites que se agreguen métodos a una clase fuera de su definición, cualquiera podría agregar en cualquier lugar , en cualquier archivo.

Eso le daría a todos los códigos de clientes acceso trivial a miembros de datos privados y protegidos.

Una vez que haya terminado la definición de la clase, no hay forma de marcar algunos archivos como especialmente bendecidos por el autor para ampliarlos, solo hay unidades de traducción planas. Por lo tanto, la única forma razonable de decirle al compilador que un conjunto particular de métodos son oficiales, o bendecidos por el autor de la clase, es declararlos dentro de la clase.

Tenga en cuenta que tenemos acceso directo a la memoria en C ++, lo que significa que generalmente es trivial crear un tipo de sombra con el mismo diseño de memoria que su clase, agregar mis propios métodos (o simplemente hacer públicos todos los datos) y reinterpret_cast . O puedo encontrar el código de su función privada, o desmontarlo. O busque la dirección de la función en la tabla de símbolos y llame o directamente.

Estos especificadores de acceso no intentan prevenir estos ataques, porque eso no es posible. Solo indican cómo se debe utilizar una clase.

    
respondido por el Useless 15.05.2014 - 17:04
6

La respuesta aceptada explica esto para virtual funciones privadas, pero eso solo responde a una faceta específica de la pregunta, que es considerablemente más limitada de lo que pidió el OP. Por lo tanto, debemos reformular: ¿por qué estamos obligados a declarar no virtuales funciones privadas en los encabezados?

Otra respuesta invoca el hecho de que las clases se deben declarar en un bloque, después de lo cual se sellan y no se pueden agregar. Eso es lo que harías al omitir declarar un método privado en el encabezado y luego tratar de definirlo en otro lugar. Buen punto ¿Por qué deberían algunos usuarios de la clase aumentarla de una manera que otros usuarios no puedan observar? Los métodos privados son parte de esto y no están excluidos de esto. Pero luego le preguntas a por qué que están incluidos, y parece un poco tautológico. ¿Por qué los usuarios de la clase deben saber sobre ellos? Si no estuvieran visibles, los usuarios no podrían agregar ninguno y están listos.

Por lo tanto, quería ofrecer una respuesta que, en lugar de simplemente incluir métodos privados de forma predeterminada, proporcione puntos específicos a favor de tenerlos visibles para los usuarios. En GotW # 100 de Herb Sutter sobre el lenguaje Pimpl como parte de su razonamiento No hablaré de Pimpl aquí, ya que estoy seguro de que todos lo sabemos. Pero aquí está el bit relevante:

  

En C ++, cuando algo en la definición de la clase de un archivo de cabecera cambia, todos los usuarios de esa clase deben volver a compilarse, incluso si el único cambio fue en los miembros de la clase privada a los que los usuarios de la clase no pueden acceder. Esto se debe a que el modelo de compilación de C ++ se basa en la inclusión textual, y porque C ++ supone que las personas que llaman saben dos cosas principales sobre una clase que pueden ser afectadas por miembros privados:

     
  • Tamaño y diseño : [de miembros y funciones virtuales: se explica por sí mismo y es excelente para el rendimiento, pero no por qué estamos aquí]
  •   
  • Funciones : el código de llamada debe poder resolver las llamadas a las funciones miembro de la clase, incluidas las funciones privadas inaccesibles que se sobrecargan con funciones no privadas. Si la función privada es una mejor coincidencia, el código que llama no podrá compilar. (C ++ tomó la decisión deliberada de diseño de realizar una resolución de sobrecarga antes de verificar la accesibilidad por razones de seguridad. Por ejemplo, se consideró que cambiar la accesibilidad de una función de privado a público no debería cambiar el significado del código de llamada legal).
  •   

Sutter es, por supuesto, una fuente extremadamente confiable como miembro del Comité, por lo que sabe "una decisión de diseño deliberada" cuando la ve. Y la idea de exigir una declaración pública de métodos privados como una forma de evitar la semántica alterada o la accesibilidad accidentalmente dañada es probablemente la razón más convincente. ¡Afortunadamente, como todo parecía un poco inútil antes!

    
respondido por el underscore_d 10.07.2016 - 10:14
4

Hay dos razones para hacer esto.

Primero, comprenda que el especificador de acceso es para el compilador y que no es relevante en el tiempo de ejecución. Acceder a un miembro privado fuera del alcance es un error compile .

Concisión

Considere una función que es corta, una o dos líneas. Existe para reducir la replicación de código en otros lugares, lo que también tiene la ventaja de poder cambiar la forma en que un algoritmo o cualquier otra cosa funciona en un solo lugar en lugar de muchos (por ejemplo, cambiar un algoritmo de clasificación).

¿Preferiría tener una o dos líneas rápidas en el encabezado, o tener la función prototipo allí más una implementación en algún lugar? Es más fácil de encontrar en el encabezado, y para funciones cortas, es mucho más detallado tener una implementación separada.

Hay otra ventaja importante, que es ...

Funciones en línea

Una función privada puede estar en línea, y esto necesariamente requiere que esté en el encabezado. Considera esto:

class A {
  private:
    inline void myPrivateFunction() {
      ...
    }

  public:
    inline void somePublicFunction() {
      myPrivateFunction();
      ...
    }
};

La función privada puede ser capaz de estar en línea junto con la función pública. Esto se hace a discreción del compilador, ya que la palabra clave inline es técnicamente una sugerencia , no un requisito.

    
respondido por el user22815 15.05.2014 - 17:00
1

Otra razón para tener métodos privados en el archivo de encabezado: hay casos en que un método público en línea no hace mucho más que llamar a uno o varios métodos privados. Tener los métodos privados en el encabezado significa que una llamada al método público puede estar completamente integrada al código real de los métodos privados, y la integración no se detiene con una llamada al método privado. Incluso desde una unidad de compilación diferente (y los métodos públicos normalmente se llamarán desde diferentes unidades de compilación).

Por supuesto, también existe la razón por la que el compilador no puede detectar problemas con la resolución de sobrecarga si no conoce todos los métodos, incluidos los privados.

    
respondido por el gnasher729 10.07.2016 - 23:20
0

Es para permitir que esas funciones accedan a los miembros privados. De lo contrario, de todos modos, necesitarías friend en el encabezado.

Si alguna función pudiera acceder a los miembros privados de la clase, la privada sería inútil.

    
respondido por el ratchet freak 15.05.2014 - 16:58

Lea otras preguntas en las etiquetas