C / C ++ inclui ordem do arquivo de cabeçalho


287

Que ordem deve incluir os arquivos a serem especificados, ou seja, quais são os motivos para incluir um cabeçalho antes do outro?

Por exemplo, os arquivos do sistema, STL e Boost vão antes ou depois do local incluir arquivos?


2
E a pletora de respostas abaixo é o motivo pelo qual os desenvolvedores Java decidiram contra cabeçalhos separados. :-) Algumas respostas realmente boas, no entanto, especialmente a advertência para garantir que seus próprios arquivos de cabeçalho possam ficar sozinhos.
Chris K

37
Eu gosto de como as perguntas com mais de 100 votos e são obviamente interessantes para algumas pessoas são encerradas como "não construtivas".
Andreas

Uma leitura altamente recomendada: cplusplus.com/forum/articles/10627
Kalsan

3
@ MRT, ASSIM lembra fortemente a comunidade nazista da sopa: Ou você segue algumas regras muito estritas ou "Nenhuma resposta / comentários adequados para você!". No entanto, se alguém tem um problema relacionado de forma alguma com a programação, este é (geralmente) o primeiro site a ir ..
Imago

Respostas:


289

Acho que não há um pedido recomendado, desde que seja compilado! O que é irritante é quando alguns cabeçalhos exigem que outros sejam incluídos primeiro ... Esse é um problema com os próprios cabeçalhos, não com a ordem de inclusões.

Minha preferência pessoal é ir do local para o global, cada subseção em ordem alfabética, ou seja:

  1. arquivo h correspondente a esse arquivo cpp (se aplicável)
  2. cabeçalhos do mesmo componente,
  3. cabeçalhos de outros componentes,
  4. cabeçalhos do sistema.

Minha justificativa para 1. é que ele deve provar que cada cabeçalho (para o qual existe um cpp) pode ser #included sem pré-requisitos (terminus technicus: o cabeçalho é "independente"). E o resto parece fluir logicamente a partir daí.


16
Praticamente o mesmo que você, exceto que eu vou de global para local, e o cabeçalho correspondente ao arquivo de origem não recebe tratamento especial.
Jon Purdy

127
@ Jon: Eu diria que é praticamente o oposto! :-) Eu diria que seu método pode introduzir dependências ocultas, por exemplo, se myclass.cpp inclui <string> e depois <myclass.h>, não há como perceber no tempo de construção que o myclass.h pode depender da string; portanto, se mais tarde você ou outra pessoa incluir myclass.h, mas não precisar de string, você receberá um erro que precisa ser corrigido no cpp ou no próprio cabeçalho. Mas eu estaria interessado em saber se é isso que as pessoas pensam que funcionaria melhor a longo prazo ... Por que você não publica uma resposta com sua proposta e veremos quem "vence"? ;-)
squelart

3
O específico para a ordenação geral é o que eu uso atualmente, a partir da recomendação de Dave Abrahams. E ele observa o mesmo motivo que o @squelart de incluir cabeçalhos ausentes nas fontes, do local ao mais geral. A chave importante é que você tem mais chances de cometer esses erros do que terceiros e bibliotecas do sistema.
GrafikRobot

7
@PaulJansen Essa é uma prática ruim e é bom usar uma técnica com maior probabilidade de explodir com ela, para que a prática ruim possa ser corrigida em vez de oculta. local para global FTW
bames53

10
@PaulJansen Sim, eu estava me referindo a anular o comportamento padrão. Isso pode acontecer por acidente, assim como, por exemplo, quebrar o ODR pode acontecer por acidente. A solução não é usar práticas que ocultam quando esses acidentes acontecem, mas usar práticas com maior probabilidade de explodi-las o mais alto possível, para que os erros possam ser percebidos e corrigidos o mais cedo possível.
usar o seguinte comando

106

O importante é ter em mente que seus cabeçalhos não devem depender de outros cabeçalhos serem incluídos primeiro. Uma maneira de garantir isso é incluir seus cabeçalhos antes de qualquer outro cabeçalho.

"Thinking in C ++" em particular menciona isso, referenciando o "Large Scale C ++ Software Design" da Lakos:

Erros de uso latente podem ser evitados, garantindo que o arquivo .h de um componente seja analisado por si só - sem declarações ou definições fornecidas externamente ... Incluindo o arquivo .h como a primeira linha do arquivo .c garante que nenhuma parte crítica Faltam informações intrínsecas à interface física do componente no arquivo .h (ou, se houver, você as descobrirá assim que tentar compilar o arquivo .c).

Ou seja, inclua na seguinte ordem:

  1. O cabeçalho do protótipo / interface para esta implementação (ou seja, o arquivo .h / .hh que corresponde a esse arquivo .cpp / .cc).
  2. Outros cabeçalhos do mesmo projeto, conforme necessário.
  3. Cabeçalhos de outras bibliotecas não padronizadas e que não são do sistema (por exemplo, Qt, Eigen, etc).
  4. Cabeçalhos de outras bibliotecas "quase padrão" (por exemplo, Boost)
  5. Cabeçalhos C ++ padrão (por exemplo, iostream, funcional etc.)
  6. Cabeçalhos C padrão (por exemplo, cstdint, dirent.h etc.)

Se algum dos cabeçalhos tiver um problema ao ser incluído nesta ordem, corrija-os (se o seu) ou não os use. Boicote bibliotecas que não escrevem cabeçalhos limpos.

O guia de estilo C ++ do Google argumenta quase ao contrário, sem realmente nenhuma justificativa; Pessoalmente, sou a favor da abordagem de Lakos.


13
A partir de agora, o Guia de estilos do Google C ++ recomenda incluir primeiro o arquivo de cabeçalho relacionado, seguindo a sugestão de Lakos.
Filip Bártek

Você não vai muito além do primeiro cabeçalho relacionado, porque depois de começar a incluir os cabeçalhos do projeto, você terá muitas dependências do sistema.
Micah

@Micah - os cabeçalhos do projeto que trazem "muitas dependências do sistema" são um design ruim, exatamente o que estamos tentando evitar aqui. O objetivo é evitar inclusões desnecessárias e dependências não resolvidas. Todos os cabeçalhos devem poder ser incluídos sem incluir outros primeiro. No caso em que um cabeçalho no projeto precisa de uma dependência do sistema, que seja - então você não inclui (e não deve) incluir a dependência do sistema após ele, a menos que o código local para esse arquivo use itens desse dep de sistema. Você não pode e não deve confiar nos cabeçalhos (até os seus) para incluir os depps do sistema que você usa.
Nathan Paul Simons

49

Sigo duas regras simples que evitam a grande maioria dos problemas:

  1. Todos os cabeçalhos (e de fato quaisquer arquivos de origem) devem incluir o que precisam. Eles não devem confiar em seus usuários, incluindo coisas.
  2. Como um complemento, todos os cabeçalhos devem incluir guardas para que não sejam incluídos várias vezes pela aplicação ambiciosa da regra 1 acima.

Eu também sigo as diretrizes de:

  1. Inclua primeiro os cabeçalhos do sistema (stdio.h, etc) com uma linha divisória.
  2. Agrupe-os logicamente.

Em outras palavras:

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

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

Embora, sendo diretrizes, isso seja subjetivo. As regras, por outro lado, imponho rigidamente, até o ponto de fornecer arquivos de cabeçalho 'wrapper' com guardas de inclusão e inclusões agrupadas, se algum desenvolvedor detestável de terceiros não assinar a minha visão :-)


6
+1 "Todos os cabeçalhos (e de fato todos os arquivos de origem) devem incluir o que precisam. Eles não devem confiar em seus usuários, incluindo itens". No entanto, muitas pessoas confiam nesse comportamento implícito de inclusão, por exemplo, com NULL e não incluem <cstddef>. É tão irritante ao tentar portar esse código e obter erros de compilação no NULL (uma razão pela qual eu apenas uso 0 agora).
stinky472

20
Por que você inclui primeiro os cabeçalhos do sistema? Seria melhor o outro porquê por causa de sua primeira regra.
jhasse

Se você usa macros de teste de recursos, sua primeira inclusão provavelmente NÃO deve ser um cabeçalho de biblioteca padrão. Portanto, por generalidade, eu diria que a política "primeiro local, depois global" é a melhor.
hmijail lamenta os demitidos em 30/0317

1
Em relação à sua primeira sugestão de "não confiar nos usuários", e as declarações de encaminhamento no arquivo de cabeçalho que não precisam que o arquivo de cabeçalho seja incluído? Ainda devemos incluir o arquivo de cabeçalho porque as declarações de encaminhamento colocam o ônus no usuário do arquivo de cabeçalho para incluir os arquivos apropriados.
Zoso 23/10

22

Para adicionar meu próprio tijolo à parede.

  1. Cada cabeçalho precisa ser auto-suficiente, o que só pode ser testado se for incluído primeiro pelo menos uma vez
  2. Não se deve modificar por engano o significado de um cabeçalho de terceiros, introduzindo símbolos (macro, tipos, etc.)

Então eu costumo ir assim:

// 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 uma linha em branco da próxima:

  • Cabeçalho correspondente a este arquivo cpp primeiro (verificação de integridade)
  • Cabeçalhos do sistema
  • Cabeçalhos de terceiros, organizados por ordem de dependência
  • Cabeçalhos do projeto
  • Cabeçalhos privados do projeto

Observe também que, além dos cabeçalhos do sistema, cada arquivo está em uma pasta com o nome de seu espaço para nome, apenas porque é mais fácil rastreá-los dessa maneira.


2
Para que outros arquivos de cabeçalho não sejam afetados por eles. Tanto pelo que esses cabeçalhos do sistema define (tanto X inclui e Windows inclui são ruins sobre #defineé que estragar outro código) e para prevenir dependências implícitas. Por exemplo, se nosso arquivo de cabeçalho da base de código foo.hrealmente depender, <map>mas em todos os lugares em que foi usado nos .ccarquivos <map>já estiver incluído, provavelmente não perceberemos. Até que alguém tentou incluir foo.hsem antes incluir <map>. E então eles ficariam irritados.

@ 0A0D: O segundo problema não é um problema na ordem aqui, porque cada um .htem pelo menos um .cppque o inclui primeiro (de fato, no meu código pessoal, o teste de unidade associado o inclui primeiro e o código fonte o inclui em seu grupo legítimo ) Não em relação a ser influenciado, se qualquer um dos cabeçalhos inclui <map>, em seguida, todos os cabeçalhos incluídos posteriormente são influenciados de qualquer maneira, por isso parece uma batalha perdida para mim.
Matthieu M.

1
Obviamente, é por isso que eu corrijo regularmente e corrijo o código antigo (ou mesmo o código mais recente), que requer uma inclusão desnecessária, porque apenas aumenta os tempos de construção.

@MatthieuM. Eu gostaria de saber a lógica por trás do seu ponto um, ie Header corresponding to this cpp file first (sanity check). Existe algo em particular se #include "myproject/example.h"for movido para o final de todas as inclusões?
MNS

1
@MNS: um cabeçalho deve ser independente, ou seja, não deve ser necessário incluir nenhum outro cabeçalho antes dele. É sua responsabilidade, como criador do cabeçalho, garantir isso, e a melhor maneira de fazer isso é ter um arquivo de origem no qual esse cabeçalho seja incluído primeiro. É fácil usar o arquivo de origem correspondente ao arquivo de cabeçalho, outra boa opção é usar o arquivo de origem de teste de unidade correspondente ao arquivo de cabeçalho, mas é menos universal (pode não haver testes de unidade).
Matthieu M.

16

Eu recomendo:

  1. O cabeçalho do módulo .cc que você está construindo. (Ajuda a garantir que cada cabeçalho no seu projeto não tenha dependências implícitas de outros cabeçalhos no seu projeto.)
  2. Arquivos de sistema C.
  3. Arquivos de sistema C ++.
  4. Plataforma / SO / outros arquivos de cabeçalho (por exemplo, win32, gtk, openGL).
  5. Outros arquivos de cabeçalho do seu projeto.

E, claro, ordem alfabética em cada seção, sempre que possível.

Sempre use declarações avançadas para evitar #includes desnecessários nos arquivos de cabeçalho.


+1, mas por que alfabético? Parece algo que pode fazer você se sentir melhor, mas não tem nenhum benefício prático.
Ben

9
Alfabética é uma ordem arbitrária, mas fácil. Você não precisa fazer a ordem alfabética, mas precisa fazer alguns pedidos para que todos façam isso de forma consistente. Descobri que ajuda a evitar duplicatas e facilita a mesclagem. E se você usar texto sublime, o F5 os solicitará para você.
2191414

14

Tenho certeza de que essa não é uma prática recomendada em nenhum lugar do mundo, mas eu gosto de alinhar o sistema por tamanho do nome do arquivo, classificado lexicamente no mesmo comprimento. Igual a:

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

Eu acho que é uma boa ideia incluir seus próprios cabeçalhos antes de outros povos, para evitar a vergonha da dependência da ordem de inclusão.


3
Eu gosto de classificar meus cabeçalhos usando uma chave que consiste na segunda, terceira e primeira letra nessa ordem :-) Portanto, vetor, conjunto, algoritmo, funcional para o seu exemplo.
21420

@paxdiablo, obrigado pela dica. Estou pensando em usá-lo, mas estou preocupado que isso possa acabar deixando a pilha de nomes de arquivos instável e com probabilidade de tombar. Quem sabe o que pode ser incluído se isso acontecer - talvez até windows.h.
Clstrfsck

40
Ordenado por comprimento ? Insanidade!
James McNellis

1
+1 para o primeiro. Na verdade, faz sentido, se você precisar localizar visualmente os cabeçalhos em um arquivo com seus olhos, é muito melhor que alfabético.
Kugel 04/04

6

Isto não é subjetivo. Verifique se os cabeçalhos não confiam em ser #included em ordem específica. Você pode ter certeza de que não importa em que ordem você inclui os cabeçalhos STL ou Boost.


1
Eu estava assumindo nenhuma dependência implícita
Anycorn

Sim, mas o compilador não pode fazer essa suposição; portanto, #include <A>, <B> nunca é o mesmo que #include <B>, <A> até que eles tenham sido compilados.
Mikhail

4

Primeiro, inclua o cabeçalho correspondente ao .cpp ... em outras palavras, source1.cppdeve incluir source1.hantes de incluir qualquer outra coisa. A única exceção em que consigo pensar é ao usar o MSVC com cabeçalhos pré-compilados; nesse caso, você é forçado a incluir stdafx.hantes de mais nada.

Raciocínio: a inclusão de source1.houtros arquivos anteriores antes garante que ele possa permanecer sozinho sem dependências. Se source1.hassumir uma dependência em uma data posterior, o compilador o alertará imediatamente para adicionar as declarações de encaminhamento necessárias source1.h. Por sua vez, isso garante que os cabeçalhos possam ser incluídos em qualquer ordem por seus dependentes.

Exemplo:

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
...

Usuários de MSVC: eu recomendo o uso de cabeçalhos pré-compilados. Portanto, mova todas as #includediretrizes para cabeçalhos padrão (e outros que nunca serão alterados) para stdafx.h.


2

Inclua do mais específico ao menos específico, começando com o .hpp correspondente para o .cpp, se houver. Dessa forma, quaisquer dependências ocultas nos arquivos de cabeçalho que não sejam auto-suficientes serão reveladas.

Isso é complicado pelo uso de cabeçalhos pré-compilados. Uma maneira de contornar isso é, sem especificar o compilador do seu projeto, é usar um dos cabeçalhos do projeto como arquivo de inclusão do cabeçalho pré-compilado.


1

É uma pergunta difícil no mundo C / C ++, com tantos elementos além do padrão.

Acho que a ordem dos arquivos de cabeçalho não é um problema sério, desde que seja compilado, como o squelart disse.

Minhas idéias são: se não houver conflito de símbolos em todos esses cabeçalhos, qualquer ordem será válida e o problema de dependência do cabeçalho poderá ser corrigido posteriormente, adicionando #include lines ao .h defeituoso.

O verdadeiro aborrecimento surge quando algum cabeçalho altera sua ação (verificando #if condições) de acordo com o que os cabeçalhos estão acima.

Por exemplo, em stddef.h no VS2005, há:

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

Agora o problema: se eu tiver um cabeçalho personalizado ("custom.h") que precise ser usado com muitos compiladores, incluindo alguns mais antigos que não fornecem offsetofem seus cabeçalhos de sistema, devo escrever no meu cabeçalho:

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

E não se esqueça de informar ao usuário que #include "custom.h" após todos os cabeçalhos do sistema, caso contrário, a linha de offsetofem stddef.h indicará um erro de redefinição de macro.

Oramos para não encontrar mais nenhum desses casos em nossa carreira.

Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.