Dove inserire le istruzioni include, l'intestazione o la fonte?


107

Devo mettere gli include nel file di intestazione o nel file di origine? Se il file di intestazione contiene le istruzioni include, se includo quel file di intestazione nella mia fonte, il mio file di origine avrà tutti i file inclusi che erano nell'intestazione? O dovrei semplicemente includerli solo nel mio file sorgente?


2
Molti precedenti duplicati su SO, ad es. Dove dovrebbe essere "include" in C ++
Paul R

Risposte:


141

Metti gli include in un'intestazione solo se l'intestazione stessa ne ha bisogno.

Esempi:

  • La tua funzione restituisce il tipo size_t. Quindi #include <stddef.h>nel file di intestazione .
  • La tua funzione utilizza strlen. Quindi #include <string.h>nel file sorgente .

2
E se la mia funzione accetta un argomento di tipo size_t?
andrybak

Stessa domanda in espansione in c ++: cosa succede se la mia struttura / classe ha un campo / membro di tipo size_to std::string?
andrybak

10
Qual è la logica?
Patrizio Bertoni

Ho una situazione cablata, la classe C ++ A ha un oggetto di un'altra classe B e non posso utilizzare la dichiarazione in avanti di B e alla fine includendo l'intestazione B all'interno dell'intestazione A. (l'utilizzo del puntatore non presenta questo problema)
shuva

@andrybak I tuoi file di origine dovrebbero includere il tuo file di intestazione in modo che anche qualsiasi inclusione dell'intestazione ottenga la tua fonte.
Jeremy Trifilo

27

Negli anni c'è stato un bel po 'di disaccordo su questo. Un tempo, era tradizionale che un'intestazione dichiarasse solo ciò che era in qualsiasi modulo a cui era correlata, quindi molte intestazioni avevano requisiti specifici che tu avessi #includeun certo insieme di intestazioni (in un ordine specifico). Alcuni programmatori C estremamente tradizionali seguono ancora questo modello (religiosamente, almeno in alcuni casi).

Più recentemente, c'è un movimento per rendere la maggior parte delle intestazioni autonome. Se quell'intestazione richiede qualcos'altro, l'intestazione stessa lo gestisce, assicurandosi che tutto ciò di cui ha bisogno sia incluso (nell'ordine corretto, se ci sono problemi di ordinamento). Personalmente, preferisco questo - specialmente quando l'ordine delle intestazioni può essere importante, risolve il problema una volta, invece di richiedere a chiunque lo utilizzi di risolvere il problema ancora una volta.

Nota che la maggior parte delle intestazioni dovrebbe contenere solo dichiarazioni. Ciò significa che l'aggiunta di un'intestazione non necessaria non dovrebbe (normalmente) avere alcun effetto sull'eseguibile finale. La cosa peggiore che accade è che rallenta un po 'la compilazione.


2
Se tutte le intestazioni sono scritte nel secondo stile, non dovrebbero esserci problemi di ordinamento. Avere problemi di ordinamento nelle intestazioni di solito significa che non hai incluso tutto ciò di cui hai bisogno nell'intestazione.
Arrivederci SE

12

I tuoi #includefile dovrebbero essere file di intestazione e ogni file (sorgente o intestazione) dovrebbe avere #includei file di intestazione di cui ha bisogno. I file di intestazione dovrebbero essere #includei file di intestazione minimi necessari e anche i file di origine dovrebbero, sebbene non sia così importante per i file di origine.

Il file sorgente avrà le intestazioni che #includes, e le intestazioni esse #include, e così via fino alla massima profondità di annidamento. Questo è il motivo per cui non vuoi messaggi superflui #includenei file di intestazione: possono far sì che un file di origine includa molti file di intestazione di cui potrebbe non aver bisogno, rallentando la compilazione.

Ciò significa che è del tutto possibile che i file di intestazione vengano inclusi due volte e questo può essere un problema. Il metodo tradizionale consiste nell'inserire "include guards" nei file di intestazione, come questo per il file foo.h:

#ifndef INCLUDE_FOO_H
#define INCLUDE_FOO_H
/* everything in header goes here */
#endif

So che questa risposta è super vecchia, ma da allora hanno aggiunto #pragma una volta, quindi non devi includere #ifndef quando dichiari #includes Ho pubblicato questo perché i thread più vecchi ma più popolari / votati verso l'alto tendono ad essere in cima alle ricerche su Google
Segugi Dogunbound il

6

L'approccio in cui mi sono evoluto in oltre vent'anni è questo;

Considera una biblioteca.

Sono presenti più file C, un file H interno e un file H esterno. I file C includono il file H. interno. Il file H interno include il file H esterno.

Vedete che dal POV del compilatore, poiché compila un file C, c'è una gerarchia;

esterno -> interno -> codice C.

Questo è l'ordine corretto, poiché ciò che è esterno è tutto ciò di cui una terza parte ha bisogno per utilizzare la libreria. Ciò che è interno è necessario per compilare il codice C.


4

Se il file di intestazione Un #includesfile header B e C, quindi ogni file di origine che #includesA sarà anche ottenere B e C #included. Il preprocessore esegue letteralmente solo la sostituzione del testo: ovunque trova del testo che dice che #include <foo.h>lo sostituisce con il testo del foo.hfile.

Ci sono opinioni diverse sull'opportunità di inserire #includesintestazioni o file sorgente. Personalmente, preferisco mettere tutto #includesnel file sorgente per impostazione predefinita, ma tutti i file di intestazione che non possono essere compilati senza altri prerequisiti dovrebbero essere #includequelli stessi.

E ogni file di intestazione dovrebbe contenere una protezione di inclusione per evitare che venga incluso più volte.


4

In alcuni ambienti, la compilazione sarà più veloce se si includono solo i file di intestazione necessari. In altri ambienti, la compilazione sarà ottimizzata se tutti i file di origine possono utilizzare la stessa raccolta primaria di intestazioni (alcuni file potrebbero avere intestazioni aggiuntive oltre al sottoinsieme comune). Idealmente, le intestazioni dovrebbero essere costruite in modo che più operazioni #include non abbiano effetto. Potrebbe essere utile racchiudere le istruzioni #include con controlli per includere-guard del file da includere, sebbene ciò crei una dipendenza dal formato di tale guardia. Inoltre, a seconda del comportamento di caching dei file di sistema, un #include non necessario il cui target finisce per essere completamente # ifdef'ed eliminato potrebbe non richiedere molto tempo.

Un'altra cosa da considerare è che se una funzione accetta un puntatore a una struttura, si può scrivere il prototipo come

void foo (struct BAR_s * bar);

senza che debba essere inclusa una definizione per BAR_s. Un approccio molto pratico per evitare include non necessari.

PS: in molti dei miei progetti, ci sarà un file che ci si aspetta che ogni modulo #includa, contenente cose come typedef per le dimensioni intere e alcune strutture e unioni comuni [es.

typedef union {
  non firmato lungo l;
  short lw senza segno [2];
  carattere senza segno lb [4];
} U_QUAD;

(Sì, so che sarei nei guai se passassi a un'architettura big-endian, ma poiché il mio compilatore non consente le strutture anonime nelle unioni, l'utilizzo di identificatori denominati per i byte all'interno dell'unione richiederebbe l'accesso come theUnion.b.b1 ecc. che sembra piuttosto fastidioso.


3

Crea tutti i tuoi file in modo che possano essere creati utilizzando solo ciò che includono. Se non hai bisogno di includere nell'intestazione, rimuovilo. In un grande progetto, se non mantieni questa disciplina, ti lasci aperto a rompere un'intera build quando qualcuno rimuove un include da un file di intestazione che viene utilizzato da un consumatore di quel file e nemmeno dall'intestazione.


1

Il tuo file sorgente avrà le istruzioni include se lo metti nell'intestazione. Tuttavia, in alcuni casi sarebbe meglio inserirli nel file sorgente.

Ricorda che se includi quell'intestazione in qualsiasi altra fonte, riceveranno anche gli include dall'intestazione, e questo non è sempre desiderabile. Dovresti includere solo le cose in cui viene utilizzato.


1

Dovresti includere solo i file nell'intestazione di cui hai bisogno per dichiarare costanti e dichiarazioni di funzione. Tecnicamente, questi include saranno inclusi anche nel file sorgente, ma per motivi di chiarezza, dovresti includere in ogni file solo i file che devi effettivamente utilizzare. Dovresti anche proteggerli nella tua intestazione dall'inclusione multipla in questo modo:

#ifndef NAME_OF_HEADER_H
#define NAME_OF_HEADER_H

...definition of header file...

#endif

Ciò impedisce che l'intestazione venga inclusa più volte, provocando un errore del compilatore.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.