Declaração de encaminhamento vs inclusão

17

Reduce the number of #include files in header files. It will reduce build times. Instead, put include files in source code files and use forward declarations in header files.

Eu li isso aqui. http://www.yolinux.com/TUTORIALS/LinuxTutorialC++CodingStyle.html .

Por isso, diz se uma classe (classe A) no arquivo de cabeçalho não precisa usar a definição real de alguma classe (classe B). Nesse momento, podemos usar a declaração direta em vez de incluir o arquivo de cabeçalho específico (classe B).

Pergunta: Se a classe (classe A) no cabeçalho se não usar a definição real de uma classe específica (classe B), então como a declaração direta ajuda a reduzir o tempo de compilação?

Nayana Adassuriya
fonte

Respostas:

11

O compilador não se importa se a classe A usa a classe B. Ele apenas sabe que quando a classe A é compilada e não possui declaração anterior da classe B (declaração direta ou outra), ela entra em pânico e a marca como erro.

O que é importante aqui é que o compilador sabe que você não tentou compilar um programa depois que seu gato entrou no teclado e criou algumas letras aleatórias que podem ou não ser interpretadas como uma classe.

Quando vê um include, para poder usar qualquer informação contida nele, ele deve abrir o arquivo e analisá-lo (independentemente de realmente precisar ou não). Se esse arquivo incluir outros arquivos, eles também deverão ser abertos e analisados, etc. Se isso puder ser evitado, geralmente é uma boa ideia usar uma declaração de encaminhamento.

Editar : a exceção a esta regra é o cabeçalho pré-compilado. Nesse caso, todos os cabeçalhos são compilados e salvos para futuras compilações. Se os cabeçalhos não forem alterados, o compilador poderá usar com inteligência os cabeçalhos pré-compilados das compilações anteriores e, assim, reduzir o tempo de compilação, mas só funcionará bem se você não precisar alterar frequentemente os cabeçalhos.

Neil
fonte
Obrigado pela explicação. Então ok como exemplo, você acha que há três arquivos de cabeçalho vehicle.h, bus.h, toybus.h. vehicle.hincluir por bus.he bus.hincluir por toybus.h. então se eu fizer alguma mudança bus.h. o compilador abre e analisa vehicle.hnovamente? ele compila de novo?
Nayana Adassuriya
1
@NayanaAdassuriya Sim, ele é incluído e analisado toda vez, e é por isso que você vê #pragma onceou #ifndef __VEHICLE_H_digita declarações nos arquivos de cabeçalho para impedir que esses arquivos sejam incluídos várias vezes (ou sejam usados ​​várias vezes, pelo menos no caso de ifndef).
23413 Neil
4

porque então A.hpp não precisa #incluir B.hpp

então A.hpp se torna

class B;//or however forward decl works for classes

class A
{
    B* bInstance_;
//...
}

portanto, quando A.hpp é incluído, o B.hpp não é incluído implicitamente e todos os arquivos que dependem apenas do A.hpp não precisam ser recompilados cada vez que o b.hpp é alterado

catraca arrepiante
fonte
mas no arquivo de origem (A.cpp). precisa incluir o arquivo de cabeçalho real (Bh). Então, toda vez que precisar compilar. Finalmente, nos dois sentidos, Bh precisa recompilar com as alterações. Diferente?
Nayana Adassuriya
@NayanaAdassuriya não porque A usa apenas um ponteiro para B e muda para B não afetará A.hpp (ou os arquivos que incluí-lo)
catraca aberração
@NayanaAdassuriya: Sim, o A.cpp terá que recompilar (se usar a definição de B dentro dos corpos dos métodos de A, mas geralmente o faz), mas o C.cpp, que usa A, mas não diretamente B, não o fará.
Jan Hudec
3

Lembre-se, o pré-processador C / C ++ é uma etapa de processamento separada, puramente textual. A #includediretiva extrai o conteúdo do cabeçalho incluído e o compilador precisa analisá-lo. Além disso, a compilação de cada um .cppé completamente separada, portanto, o fato de o compilador apenas ser analisado B.hdurante a compilação B.cppnão ajuda muito quando é necessário novamente ao compilar A.cpp. E mais uma vez durante a compilação C.cpp. E D.cpp. E assim por diante. E cada um desses arquivos precisa ser recompilado se algum arquivo incluído tiver sido alterado.

Então, digamos que classe Ausa classe Be classes Ce Duse classe A, mas não precisa manipular B. Se a classe Apuder ser declarada apenas com declaração direta de B, então B.hserá compilado duas vezes: ao compilar B.cppe A.cpp(porque Bainda é necessário dentro Ados métodos).

Mas quando A.hinclui B.h, ele é compilado quatro vezes, ao compilar B.cpp, A.cpp, C.cppe D.cppcomo o mais tarde dois agora indiretamente incluem B.htambém.

Além disso, quando cabeçalho está incluído mais de uma vez, o pré-processador ainda tem que lê-lo cada vez. Irá pular o processamento do conteúdo por causa da proteção#ifdef , mas ainda a lê e precisa procurar o final da proteção, o que significa que precisa analisar todas as diretrizes do pré-processador.

(Como mencionado na outra resposta, os cabeçalhos pré-compilados tentam contornar isso, mas eles são sua própria lata de worms; basicamente você pode razoavelmente usá-los para cabeçalhos do sistema e somente se você não estiver usando muitos deles, mas não para cabeçalhos no seu projeto)

Jan Hudec
fonte
+1, as inclusões de cabeçalho tornam-se apenas um problema sério quando você tem um número bastante grande de classes, não quando possui apenas duas classes A e B. Todas as outras postagens parecem não entender esse ponto central.
Doc Brown
2

Uma declaração de encaminhamento é muito mais rápida de analisar do que um arquivo de cabeçalho inteiro, que por si só pode incluir ainda mais arquivos de cabeçalho.

Além disso, se você alterar algo no arquivo de cabeçalho da classe B, tudo, inclusive esse cabeçalho, terá que ser recompilado. Com uma declaração de encaminhamento, esse pode ser apenas o arquivo de origem em que a implementação de A está residindo. Mas se o cabeçalho de A realmente incluísse o cabeçalho de B, tudo o que incluir a.hpptambém será recompilado, mesmo que não use nada de B.

Benjamin Kloster
fonte