C / C ++ incluye el orden del archivo de encabezado


287

¿Qué orden debe incluir los archivos que se deben especificar, es decir, cuáles son las razones para incluir un encabezado antes que otro?

Por ejemplo, ¿los archivos del sistema, STL y Boost van antes o después de los archivos de inclusión locales?


2
Y la gran cantidad de respuestas a continuación es la razón por la cual los desarrolladores de Java decidieron en contra de encabezados separados. :-) Sin embargo, algunas respuestas realmente buenas, particularmente la advertencia para asegurarse de que sus propios archivos de encabezado puedan estar solos.
Chris K

37
Me gusta cómo las preguntas que tienen más de 100 votos y son obviamente interesantes para algunas personas se cierran como "no constructivas".
Andreas

Una lectura muy recomendable: cplusplus.com/forum/articles/10627
Kalsan

3
@mrt, SO recuerda mucho a la comunidad nazi de la sopa: O sigues algunas reglas muy estrictas, o "¡No hay respuestas / comentarios adecuados para ti!". Sin embargo, si alguien tiene un problema relacionado de alguna manera con la programación, este es (por lo general) el primer sitio que debe ir ...
Imago

Respuestas:


289

¡No creo que haya un pedido recomendado, siempre que se compile! Lo que es molesto es cuando algunos encabezados requieren que se incluyan otros encabezados primero ... Eso es un problema con los encabezados en sí, no con el orden de las inclusiones.

Mi preferencia personal es ir de local a global, cada subsección en orden alfabético, es decir:

  1. archivo h correspondiente a este archivo cpp (si corresponde)
  2. encabezados del mismo componente,
  3. encabezados de otros componentes,
  4. encabezados del sistema.

Mi razonamiento para 1. es que debería probar que cada encabezado (para el cual hay un cpp) se puede #included sin requisitos previos (terminus technicus: el encabezado es "autónomo"). Y el resto parece fluir lógicamente desde allí.


16
Más o menos lo mismo que tú, excepto que voy de global a local, y el encabezado correspondiente al archivo fuente no recibe un tratamiento especial.
Jon Purdy

127
@ Jon: ¡Diría que es todo lo contrario! :-) Diría que su método puede introducir dependencias ocultas, por ejemplo, si myclass.cpp incluye <string> y luego <myclass.h>, no hay forma de detectar en tiempo de compilación que myclass.h puede depender de la cadena; así que si más adelante, usted u otra persona, incluye myclass.h pero no necesita cadena, obtendrá un error que debe corregirse en el cpp o en el encabezado. Pero me interesaría saber si eso es lo que la gente piensa que funcionaría mejor a largo plazo ... ¿Por qué no publica una respuesta con su propuesta y veremos quién "gana"? ;-)
squelart

3
Lo específico del pedido general es lo que uso en este momento por recomendación de Dave Abrahams. Y señala la misma razón que @squelart de iluminar el encabezado faltante incluye en las fuentes, desde lo local a lo más general. La clave importante es que es más probable que cometa esos errores que las bibliotecas de sistemas y de terceros.
GrafikRobot

77
@PaulJansen Esa es una mala práctica y es bueno usar una técnica que es más probable que explote con ella para que la mala práctica pueda repararse en lugar de ocultarse. FTW local a global
bames53

10
@PaulJansen Sí, me refería a anular el comportamiento estándar. Podría suceder por accidente al igual que, por ejemplo, romper el ODR puede suceder por accidente. La solución no es usar prácticas que se esconden cuando ocurren tales accidentes, sino usar las prácticas que tienen más probabilidades de hacer que exploten lo más fuerte posible, para que los errores puedan ser notados y reparados lo antes posible.
bames53

106

Lo más importante a tener en cuenta es que sus encabezados no deberían depender de que se incluyan primero otros encabezados. Una forma de asegurar esto es incluir sus encabezados antes que cualquier otro encabezado.

"Pensar en C ++" en particular menciona esto, haciendo referencia al "Diseño de software C ++ a gran escala" de Lakos:

Los errores de uso latente pueden evitarse asegurando que el archivo .h de un componente se analice por sí mismo, sin declaraciones o definiciones proporcionadas externamente ... Incluir el archivo .h como la primera línea del archivo .c asegura que ninguna pieza crítica falta información intrínseca a la interfaz física del componente en el archivo .h (o, si la hay, la descubrirá tan pronto como intente compilar el archivo .c).

Es decir, incluir en el siguiente orden:

  1. El prototipo / encabezado de interfaz para esta implementación (es decir, el archivo .h / .hh que corresponde a este archivo .cpp / .cc).
  2. Otros encabezados del mismo proyecto, según sea necesario.
  3. Encabezados de otras bibliotecas no estándar y no del sistema (por ejemplo, Qt, Eigen, etc.).
  4. Encabezados de otras bibliotecas "casi estándar" (por ejemplo, Boost)
  5. Encabezados C ++ estándar (por ejemplo, iostream, funcional, etc.)
  6. Encabezados C estándar (por ejemplo, cstdint, dirent.h, etc.)

Si alguno de los encabezados tiene un problema para ser incluido en este orden, corríjalo (si es suyo) o no lo use. Boicotee las bibliotecas que no escriben encabezados limpios.

La guía de estilo C ++ de Google argumenta casi lo contrario, sin ninguna justificación en absoluto; Yo personalmente tiendo a favorecer el enfoque de Lakos.


13
A partir de ahora, la Guía de estilo de Google C ++ recomienda incluir primero el archivo de encabezado relacionado, siguiendo la sugerencia de Lakos.
Filip Bártek

No va mucho más allá del primer encabezado relacionado porque una vez que comience a incluir sus encabezados en el proyecto, va a obtener muchas dependencias del sistema.
Micah

@Micah: los encabezados en el proyecto que incluyen "muchas dependencias del sistema" es un mal diseño, precisamente lo que estamos tratando de evitar aquí. El punto es evitar tanto las inclusiones innecesarias como las dependencias no resueltas. Todos los encabezados deben poder incluirse sin incluir otros encabezados primero. En el caso de que un encabezado dentro del proyecto necesite una dependencia del sistema, que así sea, entonces no debe (y no debe) incluir la dependencia del sistema después de esto, a menos que el código local de ese archivo use cosas de esa dep del sistema. No puede y no debe confiar en los encabezados (incluso el suyo) para incluir los departamentos del sistema que utiliza.
Nathan Paul Simons

49

Sigo dos reglas simples que evitan la gran mayoría de los problemas:

  1. Todos los encabezados (y de hecho cualquier archivo fuente) deben incluir lo que necesitan. Deben no depender de sus usuarios, incluyendo cosas.
  2. Como complemento, todos los encabezados deben incluir guardias para que no se incluyan varias veces por la aplicación demasiado ambiciosa de la regla 1 anterior.

También sigo las pautas de:

  1. Incluya los encabezados del sistema primero (stdio.h, etc.) con una línea divisoria.
  2. Agrúpelos lógicamente.

En otras palabras:

#include <stdio.h>
#include <string.h>

#include "btree.h"
#include "collect_hash.h"
#include "collect_arraylist.h"
#include "globals.h"

Aunque, siendo pautas, eso es algo subjetivo. Las reglas, por otro lado, las hago cumplir rígidamente, incluso hasta el punto de proporcionar archivos de encabezado 'wrapper' con incluir guardias e incluir agrupados si algún desarrollador desagradable de terceros no se suscribe a mi visión :-)


66
+1 "Todos los encabezados (y de hecho cualquier archivo fuente) deben incluir lo que necesitan. No deben confiar en que sus usuarios incluyan cosas". Sin embargo, muchas personas confían en este comportamiento de inclusión implícito, por ejemplo, con NULL y no incluyen <cstddef>. Es muy molesto cuando intento portar este código y obtener errores de compilación en NULL (una razón por la que solo uso 0 ahora).
stinky472

20
¿Por qué incluye primero los encabezados del sistema? Sería mejor el otro por qué debido a su primera regla.
jhasse

Si utiliza macros de prueba de características, su primera inclusión probablemente NO debería ser un encabezado de biblioteca estándar. Por lo tanto, en general, diría que la política "local primero, global después" es la mejor.
hmijail llora a los despedidos el

1
Con respecto a su primera sugerencia de "no depender de los usuarios", ¿qué pasa con las declaraciones de reenvío en el archivo de encabezado que no necesitan que se incluya el archivo de encabezado? ¿Deberíamos incluir el archivo de encabezado porque las declaraciones de reenvío recaen sobre el usuario del archivo de encabezado para incluir los archivos apropiados?
Zoso

22

Para agregar mi propio ladrillo a la pared.

  1. Cada encabezado debe ser autosuficiente, lo que solo se puede probar si se incluye primero al menos una vez
  2. No se debe modificar por error el significado de un encabezado de terceros mediante la introducción de símbolos (macro, tipos, etc.)

Por lo general, voy así:

// myproject/src/example.cpp
#include "myproject/example.h"

#include <algorithm>
#include <set>
#include <vector>

#include <3rdparty/foo.h>
#include <3rdparty/bar.h>

#include "myproject/another.h"
#include "myproject/specific/bla.h"

#include "detail/impl.h"

Cada grupo separado por una línea en blanco del siguiente:

  • Encabezado correspondiente a este archivo cpp primero (comprobación de sanidad)
  • Encabezados del sistema
  • Encabezados de terceros, organizados por orden de dependencia
  • Encabezados de proyecto
  • Proyecto de encabezados privados

También tenga en cuenta que, además de los encabezados del sistema, cada archivo está en una carpeta con el nombre de su espacio de nombres, simplemente porque es más fácil rastrearlos de esta manera.


2
Para que otros archivos de encabezado no se vean afectados por ellos. Tanto por lo que definen esos encabezados del sistema (tanto X incluye y Windows incluye son malos sobre #definelos que estropean otro código) y para evitar dependencias implícitas. Por ejemplo, si nuestro archivo de encabezado de base de código foo.hrealmente depende de, <map>pero en todas partes se usó en .ccarchivos, <map>ya estaba incluido, probablemente no lo notaremos. Hasta que alguien trató de incluir foo.hsin incluir primero <map>. Y luego se molestarían.

@ 0A0D: El segundo problema no es un problema en el orden aquí, porque cada uno .htiene al menos uno .cppque lo incluye primero (de hecho, en mi código personal, la prueba de Unidad asociada lo incluye primero, y el código fuente lo incluye en su grupo legítimo ) Con respecto a no ser influenciado, si alguno de los encabezados incluye, <map>entonces todos los encabezados incluidos después están influenciados de todos modos, por lo que me parece una batalla perdida.
Matthieu M.

1
Por supuesto, es por eso que regularmente reviso y corrijo el código anterior (o incluso el código más nuevo) que requiere una inclusión innecesaria porque solo aumenta los tiempos de compilación.

@MatthieuM. Me gustaría saber la razón detrás de su punto uno, es decir Header corresponding to this cpp file first (sanity check). ¿Hay algo en particular si #include "myproject/example.h"se mueve al final de todo incluye?
MNS

1
@MNS: un encabezado debe ser independiente, es decir, no se debe requerir que incluya ningún otro encabezado antes. Es su responsabilidad, como escritor del encabezado, asegurarse de esto, y la mejor manera de hacerlo es tener un archivo fuente en el que este encabezado se incluya primero. Usar el archivo fuente correspondiente al archivo de encabezado es fácil, otra buena opción es usar el archivo fuente de prueba de unidad correspondiente al archivo de encabezado pero es menos universal (puede que no haya pruebas unitarias).
Matthieu M.

16

Yo recomiendo:

  1. El encabezado del módulo .cc que está creando. (Ayuda a garantizar que cada encabezado en su proyecto no tenga dependencias implícitas en otros encabezados en su proyecto).
  2. C archivos del sistema.
  3. Archivos del sistema C ++.
  4. Plataforma / SO / otros archivos de encabezado (por ejemplo, win32, gtk, openGL).
  5. Otros archivos de encabezado de su proyecto.

Y, por supuesto, el orden alfabético dentro de cada sección, cuando sea posible.

Utilice siempre declaraciones de reenvío para evitar correos electrónicos innecesarios #includeen sus archivos de encabezado.


+1, pero ¿por qué alfabético? Parece algo que puede hacerte sentir mejor, pero no tiene ningún beneficio práctico.
Ben

99
Alfabético es un ordenamiento arbitrario, pero fácil. No tiene que hacer alfabéticamente, pero debe elegir algunos pedidos para que todos lo hagan de manera consistente. He descubierto que ayuda a evitar duplicados y facilita las fusiones. Y si usa texto sublime, F5 los ordenará por usted.
i_am_jorf

14

Estoy bastante seguro de que esta no es una práctica recomendada en ningún lugar del mundo cuerdo, pero me gusta alinear el sistema por longitud de nombre de archivo, ordenado léxicamente dentro de la misma longitud. Al igual que:

#include <set>
#include <vector>
#include <algorithm>
#include <functional>

Creo que es una buena idea incluir sus propios encabezados antes que otras personas, para evitar la vergüenza de la dependencia del orden de inclusión.


3
Me gusta ordenar mis encabezados usando una clave que consiste en la segunda, tercera y primera letra en ese orden :-) Entonces vector, set, Algoritmo, funcional para su ejemplo.
paxdiablo

@paxdiablo, gracias por el consejo. Estoy considerando usarlo, pero me preocupa que pueda terminar dejando la pila de nombres de archivo inestables y que puedan volcarse. Quién sabe qué podría incluirse si esto sucede, tal vez incluso windows.h.
clstrfsck

40
Ordenado por longitud ? ¡Locura!
James McNellis

1
+1 para el primero. Tiene sentido en realidad, si necesita ubicar visualmente los encabezados dentro de un archivo con los ojos, es mucho mejor que alfabético.
Kugel

6

Esto no es subjetivo. Asegúrese de que sus encabezados no dependan de estar #includeen un orden específico. Puede estar seguro de que no importa en qué orden incluya encabezados STL o Boost.



Sí, pero el compilador no puede hacer esta suposición, por lo que #include <A>, <B> nunca es lo mismo que #include <B>, <A> hasta que se hayan compilado.
Mikhail

4

Primero incluya el encabezado correspondiente al .cpp ... en otras palabras, source1.cppdebe incluir source1.hantes de incluir cualquier otra cosa. La única excepción que se me ocurre es cuando utilizo MSVC con encabezados precompilados, en cuyo caso, está obligado a incluir stdafx.hantes que nada.

Razonamiento: incluir los source1.harchivos anteriores a cualquier otro garantiza que pueda estar solo sin sus dependencias. Si source1.htoma una dependencia en una fecha posterior, el compilador lo alertará de inmediato para agregar las declaraciones de reenvío requeridas asource1.h . Esto a su vez garantiza que los dependientes puedan incluir encabezados en cualquier orden.

Ejemplo:

source1.h

class Class1 {
    Class2 c2;    // a dependency which has not been forward declared
};

source1.cpp

#include "source1.h"    // now compiler will alert you saying that Class2 is undefined
                    // so you can forward declare Class2 within source1.h
...

Usuarios de MSVC: recomiendo utilizar encabezados precompilados. Por lo tanto, mueva todas las #includedirectivas para encabezados estándar (y otros encabezados que nunca cambiarán) a stdafx.h.


2

Incluya del más específico al menos específico, comenzando con el correspondiente .hpp para el .cpp, si existe. De esa manera, se revelarán las dependencias ocultas en los archivos de encabezado que no sean autosuficientes.

Esto se complica por el uso de encabezados precompilados. Una forma de evitar esto es, sin hacer que su compilador sea específico del proyecto, es usar uno de los encabezados del proyecto como el archivo de cabecera precompilado.


1

Es una pregunta difícil en el mundo C / C ++, con tantos elementos más allá del estándar.

Creo que el orden del archivo de encabezado no es un problema grave siempre que se compile, como dijo squelart.

Mi idea es: si no hay conflicto de símbolos en todos esos encabezados, cualquier orden está bien, y el problema de dependencia del encabezado se puede solucionar más adelante agregando #include líneas al .h defectuoso.

La verdadera molestia surge cuando algún encabezado cambia su acción (marcando las condiciones #if) de acuerdo con los encabezados anteriores.

Por ejemplo, en stddef.h en VS2005, hay:

#ifdef  _WIN64
#define offsetof(s,m)   (size_t)( (ptrdiff_t)&(((s *)0)->m) )
#else
#define offsetof(s,m)   (size_t)&(((s *)0)->m)
#endif

Ahora el problema: si tengo un encabezado personalizado ("custom.h") que debe usarse con muchos compiladores, incluidos algunos más antiguos que no se proporcionan offsetofen los encabezados de sus sistemas, debería escribir en mi encabezado:

#ifndef offsetof
#define offsetof(s,m)   (size_t)&(((s *)0)->m)
#endif

Y asegúrese de decirle al usuario que #include "custom.h" después de todos los encabezados del sistema, de lo contrario, la línea de offsetofstddef.h confirmará un error de redefinición de macro.

Oramos para no encontrarnos con más de tales casos en nuestra carrera.

Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.