2009-11-09 11 views
5

C++ cabecerasC encabezados ++ - La mejor práctica al incluir

Si tengo a.cpp y Ah, así como BH, CH, DH

debo hacer:

en Ah:

#include "b.h" 
#include "c.h" 
#include "d.h" 

en a.cpp:

#include "A.h" 

o

en a.cpp:

#include "A.h" 
#include "b.h" 
#include "c.h" 
#include "d.h" 

¿Hay problemas de rendimiento? Beneficios obvios? ¿Hay algo malo acerca de esto?

Respuesta

0

Prefiero no incluyendo cabeceras en otras cabeceras - se ralentiza compilación y las autoridades educativas locales para referencia circular

+0

@mgd - Si tengo un encabezado que tiene 10 incluye y la fuente tiene 10 diferentes incluye, ¿debería mover las inclusiones del encabezado a la fuente para que la fuente tenga 20? –

+0

Ver la respuesta más completa de alex. Una excepción a esto es si tiene encabezados precompilados, en ese caso incluya todos los encabezados de plataforma/framework/OS que nunca se convertirán en un solo archivo e incluya eso en todos lados –

0

no habría problemas de rendimiento, todo se hace en tiempo de compilación, y sus cabeceras deben establecerse para que puedan' t se incluirá más de una vez.

No sé si hay una forma estándar de hacerlo, pero prefiero incluir todos los encabezados que necesito en los archivos fuente e incluirlos en los encabezados si algo en el encabezado lo necesita (por ejemplo, un typedef de un encabezado diferente)

+0

@Jeff - ah, los elementos en el encabezado pueden necesitarlo. pero ¿no lo conseguirían si todos los encabezados están en el archivo fuente? –

20

Solo debe incluir lo necesario para compilar; agregar inclusiones innecesarias dañará sus tiempos de compilación, especialmente en proyectos grandes.

Cada archivo de encabezado debe poder compilarse limpiamente por sí mismo, es decir, si tiene un archivo fuente que incluye solo ese encabezado, debe compilarse sin errores. El archivo de encabezado no debe incluir más de lo necesario para eso.

Intente utilizar las declaraciones avanzadas tanto como sea posible. Si está utilizando una clase, pero el archivo de cabecera sólo se ocupa de punteros/referencias a objetos de esa clase, entonces no hay necesidad de incluir la definición de la clase - sólo tiene que utilizar una declaración hacia adelante:

class SomeClass; 
// Can now use pointers/references to SomeClass 
// without needing the full definition 
+0

@Adam: ¿tiene un ejemplo de declaraciones directas y cuándo es apropiado usarlo y cuándo no? –

+1

Un gran consejo. También recomendaría que el archivo .c asociado con .h incluya .h como primer include, always. Esto es para asegurarse de que todos sus archivos .h siempre puedan compilarse por sí mismos (en lugar de confiar en el orden de inclusión en su archivo .c). –

+0

+1 para la última parte, siempre he incluido previamente el archivo de cabecera –

3

Lo que Adam Rosenfield te dijo es perfecto. Un ejemplo de cuando se puede usar una declaración hacia adelante es:

#ifndef A_H_ 
#define A_H_ 

    #include "D.h" 
    class B; //forward declaration 
    class C; //forward declaration 

    class A 
    { 
    B *m_pb; //you can forward declare this one bacause it's a pointer and the compilier doesn't need to know the size of object B at this point in the code. include B.h in the cpp file. 
    C &m_rc; //you can also forware declare this one for the same reason, except it's a reference. 

    D m_d; //you cannot forward declare this one because the complier need to calc the size of object D. 

    }; 

#endif 
+0

(espero que compila, no lo probé, jajajaja. Avísame si tienes problemas con él). – cchampion

6

La práctica clave aquí es tener alrededor de cada foo.h presentar una guardia tales como:

#ifndef _FOO_H 
#define _FOO_H 

...rest of the .h file... 
#endif 

Esto evita-varios inclusiones, con bucles y todos esos horrores acompañantes. Una vez que asegure que cada archivo incluido esté protegido, los detalles son menos importantes.

Me gusta un principio rector Adam expresa: asegúrese de que, si un archivo fuente solo incluye a.h, no obtendrá inevitablemente errores debido a a.h suponiendo que se hayan incluido otros archivos antes, p. si a.h requiere que se incluya b.h antes, puede y debe incluir b.h en sí mismo (los guardias lo convertirán en noop si b.h era ya incluido anteriormente)

Viceversa, un archivo de origen debe incluir las cabeceras de la que está exigiendo algo (macros, declaraciones, etc.), no suponer que otras cabeceras acaba de llegar mágicamente porque tiene incluido algunos.

Si está utilizando clases por valor, por desgracia, necesita todos los detalles sangrientos de la clase en algunos .h que incluye. Pero para algunos usos a través de referencias o punteros, bastará con un simple class sic;. Por ejemplo, si la otra clase es igual, si la clase a puede salirse con un puntero a una instancia de clase b (es decir, un miembro class b *my_bp; en vez de un miembro class b *my_b;), el acoplamiento entre los archivos include puede debilitarse (reduciendo mucha recompilación) -- p.ej b.h podían tener mucha más class b; mientras que todos los detalles morbosos están en b_impl.h que se incluye sólo por las cabeceras que realmente lo necesitan ...

+0

Dado que foo.h aún se leerá cada vez que se incluya, no es realmente una operación no operativa. No funcionaría si lo hiciera: #ifndef _FOO_H #include "foo.h" #endif – Bill

+0

Sí, pero eso contaminaría todas las inclusiones en ilegibilidad. A todos los efectos, se incluirá una inclusión repetida en el caché del sistema de archivos, por lo que no hay ningún impacto de rendimiento en "incluirlo" de nuevo, y definitivamente no es operativo desde el punto de vista de la semántica, que es lo que realmente asuntos. –

3

Respuesta: Vamos A.h incluyen b.h, c.h y d.h sólo si se necesitaba hacer que la construcción tenga éxito La regla de oro es: solo incluir tanto código en un archivo de encabezado como sea necesario. Todo lo que no se necesite de inmediato en A.h debe incluirse en A.cpp.

Razonamiento: Mientras menos código se incluya en los archivos de encabezado, es menos probable que deba volver a compilar el código que usa el archivo de encabezado después de hacer algún cambio en alguna parte. Si hay muchas referencias #include entre diferentes archivos de encabezado, cambiar cualquiera de ellas requerirá la reconstrucción de todos los demás archivos que incluyen el archivo de encabezado modificado - recursivel. Por lo tanto, si decide tocar algún archivo de encabezado de nivel, esto podría reconstruir grandes partes de su código.

Al usar forward declarations siempre que sea posible en los archivos de encabezado, se reduce el acoplamiento de los archivos de origen y, por lo tanto, la compilación es más rápida. Tales declaraciones directas pueden usarse en muchas más situaciones de las que se podría pensar. Como regla general, sólo es necesario el archivo de cabecera t.h (que define un tipo T) si

  1. declara una variable miembro de tipo T (nota, esto no incluye declarar un puntero-a T).
  2. Escriba algunas funciones en línea que acceden a los miembros de un objeto del tipo T.

Haces no necesidad de incluir la declaración de T si su archivo de cabecera simplemente

  1. Declara constructores/funciones que tienen referencias o punteros a un objeto T.
  2. Declara funciones que devuelven un objeto T por puntero, referencia o valor.
  3. Declara las variables miembro que son referencias o punteros a un objeto T.

Considere esta declaración de clase; cuál de los archivos de inclusión para A, B y C lo que realmente necesita para incluir ?:

class MyClass 
{ 
public: 
    MyClass(const A &a); 

    void set(const B &b); 
    void set(const B *b); 
    B getB(); 
    C getC(); 

private: 
    B *m_b; 
    C m_c; 
}; 

Sólo se necesita el archivo de inclusión para el tipo C, debido a la variable miembro m_c. A menudo también puede eliminar este requisito al no declarar sus variables miembro directamente, pero usando un opaque pointer para ocultar todas las variables miembro en una estructura privada, de modo que ya no aparezcan en el archivo de encabezado.

+0

Está mezclando A, B y C con X, Y y Z en su ejemplo. – Bill

+0

@Bill: Tienes razón, ajusté el ejemplo para decir A, B, C ahora. –

+0

@FrerichRaabe respuesta perfecta. +1 para la parte "Razonamiento:" – ParokshaX

Cuestiones relacionadas