Esse é um desses tipos de padrões de codificação "deveria", em vez de "deve". O motivo é que você precisaria praticamente escrever um analisador de C ++ para aplicá-lo.
Uma regra muito comum para os arquivos de cabeçalho é que eles devem permanecer sozinhos. Um arquivo de cabeçalho não deve exigir que outros arquivos de cabeçalho sejam #incluídos antes de incluir o cabeçalho em questão. Este é um requisito testável. Dado algum cabeçalho aleatório foo.hh
, o seguinte deve compilar e executar:
#include "foo.hh"
int main () {
return 0;
}
Esta regra tem consequências no que diz respeito ao uso de outras classes em algum cabeçalho. Às vezes, essas consequências podem ser evitadas através da declaração direta dessas outras classes. Isso não é possível com muitas classes de biblioteca padrão. Não há como encaminhar uma instanciação de modelo como std::string
ou std::vector<SomeType>
. Você precisa #include
desses cabeçalhos STL no cabeçalho, mesmo que o único uso do tipo seja como argumento para uma função.
Outro problema está nas coisas que você arrasta acidentalmente. Exemplo: considere o seguinte:
arquivo foo.cc:
#include "foo.hh"
#include "bar.hh"
void Foo::Foo () : bar() { /* body elided */ }
void Foo::do_something (int item) {
...
bar.add_item (item);
...
}
Aqui bar
é uma classe Foo
membro de dados que é do tipo Bar
. Você fez a coisa certa aqui e #included bar.hh, embora isso devesse ter sido incluído no cabeçalho que define a classe Foo
. No entanto, você não incluiu o material usado por Bar::Bar()
e Bar::add_item(int)
. Existem muitos casos em que essas chamadas podem resultar em referências externas adicionais.
Se você analisar foo.o
com uma ferramenta como nm
, parece que as funções foo.cc
estão chamando todos os tipos de coisas para as quais você não fez o apropriado #include
. Então, você deve adicionar #include
diretrizes para essas referências externas incidentais foo.cc
? A resposta é absolutamente não. O problema é que é muito difícil distinguir as funções que são chamadas incidentalmente daquelas que são chamadas diretamente.
#include "x.h"
funcionará sem exigir precedentes#include
. Isso é bom o suficiente se você não abusar#define
.Se você precisar aplicar uma regra de que determinados arquivos de cabeçalho devem permanecer por si mesmos, use as ferramentas que já possui. Crie um makefile básico que compila cada arquivo de cabeçalho individualmente, mas não gera um arquivo de objeto. Você poderá especificar em qual modo compilar o arquivo de cabeçalho (modo C ou C ++) e verificar se ele pode ser independente. Você pode supor que a saída não contém nenhum falso positivo, que todas as dependências necessárias são declaradas e que a saída é precisa.
Se você estiver usando um IDE, ainda poderá realizar isso sem um makefile (dependendo do seu IDE). Basta criar um projeto adicional, adicionar os arquivos de cabeçalho que deseja verificar e alterar as configurações para compilá-lo como um arquivo C ou C ++. No MSVC, por exemplo, você alteraria a configuração "Item Type" em "Configuration Properties-> General".
fonte
Eu não acho que essa ferramenta exista, mas ficaria feliz se alguma outra resposta me refutasse.
O problema ao escrever uma ferramenta desse tipo é que ela relata com facilidade um resultado falso, então eu estimo a vantagem líquida de tal ferramenta em quase zero.
A única maneira de uma ferramenta funcionar é se ela redefinir sua tabela de símbolos apenas para o conteúdo do arquivo de cabeçalho processado, mas você terá o problema de que os cabeçalhos que formam a API externa de uma biblioteca delegam as declarações reais para cabeçalhos internos.
Por exemplo,
<string>
na implementação libc ++ do GCC, não declara nada, mas inclui apenas um monte de cabeçalhos internos que contêm as declarações reais. Se a ferramenta redefinir sua tabela de símbolos exatamente para o que foi declarado por<string>
si só, isso não seria nada.Você pode ter a ferramenta para diferenciar entre
#include ""
e#include <>
, mas isso não ajuda se uma biblioteca externa usa#include ""
para incluir seus cabeçalhos internos na API.fonte
não
#Pragma once
consegue isso? Você pode incluir algo quantas vezes quiser, diretamente ou via inclusões encadeadas, e desde que haja um#Pragma once
próximo a cada um deles, o cabeçalho será incluído apenas uma vez.Quanto à imposição, talvez você possa criar um sistema de compilação que inclua cada cabeçalho por si só com alguma função principal fictícia, apenas para garantir que ele seja compilado.
#ifdef
A cadeia inclui para obter melhores resultados com esse método de teste.fonte
Sempre inclua arquivos de cabeçalho no arquivo CPP. Isso não apenas reduz significativamente o tempo de compilação, mas também poupa muitos problemas se você optar por usar cabeçalhos pré-compilados. Na minha experiência, vale a pena praticar o trabalho de fazer declarações avançadas. Quebre a regra somente quando necessário.
fonte
Eu diria que existem vantagens e desvantagens nesta convenção. Por um lado, é bom saber exatamente o que seu arquivo .cpp está incluindo. Por outro lado, a lista de inclusões pode crescer facilmente para um tamanho ridículo.
Uma maneira de incentivar essa convenção não é incluir nada em seus próprios cabeçalhos, mas apenas nos arquivos .cpp. Portanto, qualquer arquivo .cpp usando o cabeçalho não será compilado, a menos que você inclua explicitamente todos os outros cabeçalhos dos quais depende.
Provavelmente, algum compromisso razoável está em ordem aqui. Por exemplo, você pode decidir que não há problema em incluir cabeçalhos de biblioteca padrão em seus próprios cabeçalhos, mas não mais.
fonte