Onde colocar declarações de inclusão, cabeçalho ou fonte?

107

Devo colocar as inclusões no arquivo de cabeçalho ou no arquivo de origem? Se o arquivo de cabeçalho contiver as instruções de inclusão, se eu incluir esse arquivo de cabeçalho em minha fonte, meu arquivo de fonte terá todos os arquivos incluídos que estavam em meu cabeçalho? Ou devo apenas incluí-los apenas no meu arquivo de origem?

Mohit Deshpande
fonte
2
Muitas duplicatas anteriores no SO, por exemplo, onde deve "incluir" ser colocado em C ++
Paul R
1
possível duplicata de Devo usar #include nos cabeçalhos?
Schot

Respostas:

141

Só coloque includes em um cabeçalho se o próprio cabeçalho precisar deles.

Exemplos:

  • Sua função retorna tipo size_t. Em seguida, #include <stddef.h>no arquivo de cabeçalho .
  • Sua função usa strlen. Em seguida, #include <string.h>no arquivo de origem .
schot
fonte
2
E se minha função receber um argumento do tipo size_t?
andrybak de
A mesma pergunta se expandindo para c ++: e se minha estrutura / classe tiver um campo / membro do tipo size_tou std::string?
andrybak de
10
Qual é o motivo?
Patrizio Bertoni
Eu tenho uma situação com fio, a classe A do C ++ tem um objeto de outra classe B e não posso usar a declaração de encaminhamento de B e terminar incluindo o cabeçalho B dentro do cabeçalho A. (usar o ponteiro não tem esse problema)
shuva
@andrybak Seus arquivos de origem devem incluir seu arquivo de cabeçalho, de forma que qualquer um que inclua seus arquivos de cabeçalho também receberá sua fonte.
Jeremy Trifilo
27

Tem havido um grande desacordo sobre isso ao longo dos anos. Ao mesmo tempo, era tradicional que um cabeçalho apenas declarasse o que estava em qualquer módulo ao qual estava relacionado, portanto, muitos cabeçalhos tinham requisitos específicos para #includeum determinado conjunto de cabeçalhos (em uma ordem específica). Alguns programadores C extremamente tradicionais ainda seguem esse modelo (religiosamente, pelo menos em alguns casos).

Mais recentemente, há um movimento no sentido de tornar a maioria dos cabeçalhos autônomos. Se esse cabeçalho exigir algo mais, o próprio cabeçalho lida com isso, garantindo que tudo o que for necessário seja incluído (na ordem correta, se houver problemas de pedido). Pessoalmente, eu prefiro isso - especialmente quando a ordem dos cabeçalhos pode ser importante, ela resolve o problema uma vez, em vez de exigir que todos que a usam resolvam o problema novamente.

Observe que a maioria dos cabeçalhos deve conter apenas declarações. Isso significa que adicionar um cabeçalho desnecessário não deve (normalmente) ter qualquer efeito em seu executável final. O pior que acontece é que torna a compilação um pouco mais lenta.

Jerry Coffin
fonte
2
Se todos os cabeçalhos forem escritos no segundo estilo, não deve haver nenhum problema de ordenação. Ter problemas de ordenação nos cabeçalhos geralmente significa que você não incluiu tudo o que precisa no cabeçalho.
Adeus SE
12

Seus #includes devem ser de arquivos de cabeçalho, e cada arquivo (fonte ou cabeçalho) deve #includeconter os arquivos de cabeçalho de que necessita. Arquivos de cabeçalho devem#include o mínimo de arquivos de cabeçalho necessário, e os arquivos de origem também, embora não sejam tão importantes para os arquivos de origem.

O arquivo de origem terá os cabeçalhos it #includes, e os cabeçalhos they #include, e assim por diante, até a profundidade máxima de aninhamento. É por isso que você não quer supérfluo#include s em arquivos de cabeçalho: eles podem fazer com que um arquivo de origem inclua muitos arquivos de cabeçalho de que não precisa, tornando a compilação mais lenta.

Isso significa que é perfeitamente possível que os arquivos de cabeçalho sejam incluídos duas vezes e isso pode ser um problema. O método tradicional é colocar "incluir guardas" em arquivos de cabeçalho, como este para o arquivo foo.h:

#ifndef INCLUDE_FOO_H
#define INCLUDE_FOO_H
/* everything in header goes here */
#endif
David Thornley
fonte
Eu sei que esta resposta é muito antiga, mas desde então eles adicionaram #pragma uma vez, então você não deve incluir #ifndef ao declarar #includes Eu postei esta resposta porque tópicos mais antigos, mas mais populares / votados tendem a estar no topo das pesquisas do Google
Dogunbound hounds
6

A abordagem que evoluí para mais de vinte anos é esta;

Considere uma biblioteca.

Existem vários arquivos C, um arquivo H interno e um arquivo H externo. Os arquivos C incluem o arquivo H interno. O arquivo H interno inclui o arquivo H externo.

Você vê que no POV dos compiladores, conforme ele compila um arquivo C, há uma hierarquia;

externo -> interno -> código C

Essa é a ordem correta, pois o externo é tudo o que um terceiro precisa para usar a biblioteca. Aquilo que é interno é necessário para compilar o código C.


fonte
4

Se o arquivo de cabeçalho A for #includesarquivos de cabeçalho B e C, todos os arquivos de origem que #includesA também receberão B e C#included . O pré-processador literalmente apenas executa a substituição de texto: em qualquer lugar ele encontra um texto que diz #include <foo.h>que o substitui pelo texto do foo.harquivo.

Existem diferentes opiniões sobre se você deve colocar #includes cabeçalhos ou arquivos de origem. Pessoalmente, prefiro colocar todos #includesno arquivo de origem por padrão, mas quaisquer arquivos de cabeçalho que não possam ser compilados sem outros cabeçalhos de pré-requisito devem ser #includeesses próprios cabeçalhos.

E cada arquivo de cabeçalho deve conter uma proteção de inclusão para evitar que seja incluído várias vezes.

Vicky
fonte
4

Em alguns ambientes, a compilação será mais rápida se incluirmos apenas os arquivos de cabeçalho necessários. Em outros ambientes, a compilação será otimizada se todos os arquivos de origem puderem usar a mesma coleção primária de cabeçalhos (alguns arquivos podem ter cabeçalhos adicionais além do subconjunto comum). O ideal é que os cabeçalhos sejam construídos de forma que várias operações #include não tenham efeito. Pode ser bom cercar as instruções #include com verificações para a proteção de inclusão do arquivo a ser incluído, embora isso crie uma dependência do formato dessa proteção. Além disso, dependendo do comportamento de cache de arquivos do sistema, um #include desnecessário cujo alvo acaba sendo completamente # definido pode não demorar muito.

Outra coisa a considerar é que se uma função leva um ponteiro para uma estrutura, pode-se escrever o protótipo como

void foo (struct BAR_s * bar);

sem uma definição para BAR_s tendo que estar no escopo. Uma abordagem muito útil para evitar inclusões desnecessárias.

PS - em muitos de meus projetos, haverá um arquivo que se espera que cada módulo inclua #include, contendo coisas como typedefs para tamanhos inteiros e algumas estruturas e uniões comuns [por exemplo

typedef union {
  sem sinal longo l;
  curto sem sinal lw [2];
  char lb sem sinal [4];
} U_QUAD;

(Sim, eu sei que teria problemas se mudasse para uma arquitetura big-endian, mas como meu compilador não permite estruturas anônimas em uniões, usar identificadores nomeados para os bytes dentro da união exigiria que eles fossem acessados ​​como theUnion.b.b1 etc. o que parece bastante irritante.

supergato
fonte
3

Faça todos os seus arquivos para que possam ser construídos usando apenas o que incluem. Se você não precisa incluir em seu cabeçalho, remova-o. Em um grande projeto, se você não mantiver essa disciplina, ficará aberto a quebrar uma compilação inteira quando alguém remover um include de um arquivo de cabeçalho que está sendo usado por um consumidor desse arquivo e nem mesmo pelo cabeçalho.

repetir
fonte
1

Seu arquivo de origem terá as instruções de inclusão se você colocá-lo no cabeçalho. No entanto, em alguns casos, seria melhor colocá-los no arquivo de origem.

Lembre-se de que se você incluir esse cabeçalho em qualquer outra fonte, eles também obterão as inclusões do cabeçalho, e isso nem sempre é desejável. Você só deve incluir o material onde for usado.

Splashdust
fonte
1

Você só deve incluir arquivos em seu cabeçalho que você precisa para declarar constantes e declarações de função. Tecnicamente, essas inclusões também serão incluídas em seu arquivo de origem, mas para fins de clareza, você só deve incluir em cada arquivo os arquivos que realmente precisa usar. Você também deve protegê-los em seu cabeçalho de inclusão múltipla, desta forma:

#ifndef NAME_OF_HEADER_H
#define NAME_OF_HEADER_H

...definition of header file...

#endif

Isso evita que o cabeçalho seja incluído várias vezes, resultando em um erro do compilador.

JRam930
fonte