2009-05-17 19 views
14

Me pregunto, ¿cuál es el objetivo de separar las clases en un archivo .h y un archivo .cpp? Hace que sea más difícil de editar, y si su clase no se compilará en .lib o .dll para uso externo, ¿cuál es el punto?¿Por qué poner una declaración y definición de clase en dos archivos separados en C++?

Edit: La razón por la que estoy preguntando es que las bibliotecas de Boost ponen todo en un archivo .hpp, (la mayoría de las bibliotecas de todos modos), y quería saber por qué está separado en la mayoría del otro código que Ya veo.

Respuesta

15

C++ tiene algo llamado la regla de una sola definición. Significa que (excluyendo las funciones en línea), las definiciones solo pueden aparecer en una unidad de compilación. Dado que los archivos de encabezado de C++ son simplemente "copiar y pegar" en cada archivo de inclusión, ahora está poniendo definiciones en varios lugares si solo pone las definiciones en los archivos de encabezado.

Por supuesto, usted puede decir, ¿por qué no hacer todo en línea. Bueno, si el compilador respeta su sugerencia en línea, el código para las funciones largas se replicará en cada sitio de llamadas, lo que hará que su código sea excesivamente grande y posiblemente cause molestias, problemas de caché y todo tipo de cosas no comprometidas.

Por otro lado, si el compilador no lo escucha, y no alinea nada, ahora tiene 2 problemas: 1) usted no sabe qué unidad de traducción obtuvo las definiciones de sus clases, y 2) el el compilador todavía tiene que pasar por sus definiciones cada vez que #incluye. Además, no hay una manera fácil de asegurarse de no haber definido accidentalmente el mismo método dos veces, en 2 encabezados diferentes, de manera diferente.

También obtiene un problema de dependencia circular. Para que una clase invoque el método de otra clase, esa clase debe declararse primero. Entonces, si 2 clases necesitan invocar los métodos de cada una, cada una debe declararse antes de que se pueda definir cualquiera de ellas. No hay forma de hacer esto con declaraciones y definiciones en un archivo.

Realmente, así es como se construyeron el lenguaje y el analizador. Es un dolor, pero solo tienes que lidiar con eso.

0

Porque en la mayoría de los casos, querrá utilizar la clase en algún lugar además del archivo en el que la implementa. Si haces todo el programa en un archivo, no necesitas la separación.

Casi nunca desea escribir un programa C++ todo en un solo archivo.

+0

Me refiero a poner toda la definición de clase en .h e incluirla, mientras lo protejo con guardias de encabezado –

+0

¿qué sucede si incluye ese archivo .h en más de un lugar en el mismo ejecutable? Resp: terminas con múltiples definiciones de las mismas funciones. - El verdadero punto aquí es que puedes hacer todo tipo de cosas tontas con las reglas, pero casi siempre resultan no funcionar tan bien en la práctica como en las formas normales. –

3

Bueno, uno de los beneficios de tener el código de esta manera es que reduce el tiempo de compilación.

Digamos que usted tiene estos archivos en su proyecto:

  • ah
  • a.cpp
  • b.cpp

Si ya tiene a.cpp compilado en un objeto archivo ao, luego si incluye ah en b.cpp, compilati on debería ser más rápido porque el analizador no tendrá que lidiar con toda la declaración/definición de a.

2

Porque incluso dentro de su DLL otras clases utilizarán su clase. Esos archivos deben ver la declaración de clase en tiempo de compilación, incluyendo el .h. No deben ver la definición o habrá múltiples definiciones de las funciones de clase.

0

En C++, la compilación separada de módulos de código (archivos .c o .cpp) requiere que los prototipos de función se definan antes del uso. Si desea utilizar clases o métodos definidos en otro lugar, debe importar los archivos .h para obtener su definición. Al final, el enlazador se asegura de que todas las promesas hechas en los archivos .h puedan cumplirse con todos los archivos c/cpp.

Además, permite crear marcos completos tales como aumentar solo definiendo archivos .h.

+1

Boost comprende en su mayoría archivos de encabezado ya que depende en gran medida de las plantillas de C++ (lo que implica poner toda una definición de clase en su encabezado cuando está modelado, por ejemplo). Por lo que recuerdo, parte de este marco tiene un archivo cpp, el que no se basa en plantillas de C++: la biblioteca de hilos, por ejemplo. –

2

Su edición re: Boost hace una distinción importante. Las clases de plantilla casi siempre se definen en los encabezados debido a la forma en que el compilador y el enlazador funcionan en el estándar actual de C++. Encontrará que la mayoría de las bibliotecas de plantillas (no solo Boost) se implementan en archivos de encabezado por el mismo motivo.

5

Boost no incorpora todo su código; Enumera las definiciones de plantilla para las clases que espera que sus consumidores creen, como shared_ptr. Muchas bibliotecas tienen secciones que deben compilarse por separado, como boost :: serialization y program_options.

La alineación puede tener graves efectos negativos a medida que aumenta el tamaño de la base de códigos. Aumenta el acoplamiento entre tus componentes, sin mencionar el nuking de tu tiempo de compilación (lo que aumenta por muchos otros motivos :). Efectivamente, todas sus unidades de traducción tendrían casi una copia completa del programa, y ​​un pequeño cambio podría hacer que reconstruya/vuelva a probar todo. En algunos proyectos, esto podría tomar muchas, muchas horas.

Nunca he notado realmente que sea más difícil de editar; en mi experiencia, lo hace más fácil debido a la clara separación de la interfaz y la implementación, y sé qué archivo buscar para encontrar lo que estoy buscando.

+0

¡Buena respuesta! Solo agregaría que lamentablemente la separación de la interfaz y la implementación no es tan clara como uno desearía, ya que parte de la implementación se filtra al encabezado en forma de campos de datos privados (porque de lo contrario el compilador no conocería el tamaño de la clase). El idioma pimpl se usa para abordar este problema. –

Cuestiones relacionadas