C / C ++ include l'ordine dei file di intestazione


287

In quale ordine devono essere specificati i file, ovvero quali sono i motivi per includere un'intestazione prima di un'altra?

Ad esempio, i file di sistema, STL e Boost vanno prima o dopo i file di inclusione locali?


2
E la pletora di risposte di seguito è il motivo per cui gli sviluppatori Java hanno deciso di non utilizzare intestazioni separate. :-) Alcune risposte davvero buone, tuttavia, in particolare l'ammonizione di assicurarsi che i tuoi file di intestazione possano essere autonomi.
Chris K,

37
Mi piace come le domande che hanno più di 100 voti e siano ovviamente ovvie per alcune persone vengano chiuse come "non costruttive".
Andreas,

Una lettura altamente raccomandata: cplusplus.com/forum/articles/10627
Kalsan

3
@mrt, SO ricorda fortemente la minestra della comunità nazista: O segui alcune regole molto rigide, o "Nessuna risposta / commenti adeguati per te!". Tuttavia, se qualcuno ha un problema legato in qualche modo alla programmazione, questo è (di solito) il primo sito ad andare ..
Imago

Risposte:


289

Non credo che ci sia un ordine consigliato, purché si compili! La cosa fastidiosa è quando alcune intestazioni richiedono che le altre intestazioni vengano incluse per prime ... Questo è un problema con le intestazioni stesse, non con l'ordine delle inclusioni.

La mia preferenza personale è quella di passare dal locale al globale, ogni sottosezione in ordine alfabetico, ovvero:

  1. h file corrispondente a questo file cpp (se applicabile)
  2. intestazioni dallo stesso componente,
  3. intestazioni di altri componenti,
  4. intestazioni di sistema.

La mia logica per 1. è che dovrebbe dimostrare che ogni intestazione (per la quale esiste un cpp) può essere #included senza prerequisiti (terminus technicus: l'intestazione è "autonoma"). E il resto sembra fluire logicamente da lì.


16
Praticamente lo stesso di te, tranne che vado dal globale al locale e l'intestazione corrispondente al file sorgente non riceve un trattamento speciale.
Jon Purdy,

127
@Jon: direi che è praticamente il contrario! :-) Direi che il tuo metodo può introdurre dipendenze nascoste, ad esempio se myclass.cpp include <string> quindi <myclass.h>, non c'è modo di catturare al momento della compilazione che myclass.h potrebbe dipendere dalla stringa; quindi se in seguito tu o qualcun altro includi myclass.h ma non hai bisogno di string, otterrai un errore che deve essere corretto nel cpp o nell'intestazione stessa. Ma sarei interessato a sapere se questo è ciò che la gente pensa che funzionerebbe meglio a lungo termine ... Perché non pubblichi una risposta con la tua proposta e vedremo chi "vince"? ;-)
squelart

3
Lo specifico all'ordinamento generale è quello che uso in questo momento dalla raccomandazione di Dave Abrahams. E nota la stessa ragione di @squelart di illuminare l'intestazione mancante che include nelle fonti, dal locale al più generale. L'importante è che è più probabile che tu commetta questi errori rispetto alle terze parti e alle librerie di sistema.
GrafikRobot

7
@PaulJansen Questa è una cattiva pratica ed è bene usare una tecnica che è più probabile che esploda con essa in modo che la cattiva pratica possa essere riparata invece di nascondersi. FTW locale a globale
bames53

10
@PaulJansen Sì, mi riferivo al superamento del comportamento standard. Potrebbe accadere per caso così come, ad esempio, la rottura dell'ODR può avvenire per caso. La soluzione non è quella di utilizzare le pratiche che si nascondono quando si verificano tali incidenti, ma di utilizzare le pratiche che hanno maggiori probabilità di farle esplodere il più forte possibile, in modo che gli errori possano essere rilevati e risolti il ​​più presto possibile.
bames53

106

La cosa importante da tenere a mente è che le intestazioni non dovrebbero dipendere da altre intestazioni incluse per prime. Un modo per assicurare ciò è includere le intestazioni prima di qualsiasi altra intestazione.

"Thinking in C ++" in particolare menziona questo, riferendosi al "Design software su larga scala C ++" di Lakos:

Gli errori di utilizzo latenti possono essere evitati assicurando che il file .h di un componente analizzi da solo - senza dichiarazioni o definizioni fornite esternamente ... Includendo il file .h come prima riga del file .c si assicura che nessun pezzo critico delle informazioni intrinseche all'interfaccia fisica del componente mancano dal file .h (o, se esiste, che lo scoprirai non appena provi a compilare il file .c).

Vale a dire, includere nel seguente ordine:

  1. L'intestazione prototipo / interfaccia per questa implementazione (ovvero il file .h / .hh che corrisponde a questo file .cpp / .cc).
  2. Altre intestazioni dello stesso progetto, se necessario.
  3. Intestazioni da altre librerie non standard, non di sistema (ad esempio Qt, Eigen, ecc.).
  4. Intestazioni da altre librerie "quasi standard" (ad esempio Boost)
  5. Intestazioni C ++ standard (ad esempio, iostream, funzionali, ecc.)
  6. Intestazioni C standard (ad esempio, cstdint, dirent.h, ecc.)

Se una delle intestazioni ha un problema con l'inserimento in questo ordine, correggili (se i tuoi) o non utilizzarli. Librerie di boicottaggio che non scrivono intestazioni pulite.

La guida di stile C ++ di Google sostiene quasi il contrario, senza alcuna giustificazione; Personalmente tendo a favorire l'approccio Lakos.


13
Al momento, la Guida allo stile di Google C ++ consiglia di includere prima il relativo file di intestazione, seguendo il suggerimento di Lakos.
Filip Bártek,

Non vai molto oltre la prima intestazione correlata perché una volta che inizi a includere le intestazioni nel progetto, otterrai molte dipendenze del sistema.
Michea il

@Micah - le intestazioni nel progetto che attirano "molte dipendenze di sistema" sono cattive progettazioni, esattamente ciò che stiamo cercando di evitare qui. Il punto è evitare sia le inclusioni non necessarie sia le dipendenze irrisolte. Tutte le intestazioni dovrebbero poter essere incluse senza prima includere altre intestazioni. Nel caso in cui un'intestazione nel progetto abbia bisogno di una dipendenza del sistema, così sia - allora non devi (e non dovresti) includere la dipendenza del sistema dopo di essa, a meno che il codice locale a quel file non usi cose da quel dep di sistema. Non puoi e non devi fare affidamento sulle intestazioni (anche le tue) per includere i deps di sistema che usi.
Nathan Paul Simons,

49

Seguo due semplici regole che evitano la stragrande maggioranza dei problemi:

  1. Tutte le intestazioni (e in effetti qualsiasi file di origine) dovrebbero includere ciò di cui hanno bisogno. Non dovrebbero fare affidamento sui propri utenti, comprese le cose.
  2. In aggiunta, tutte le intestazioni dovrebbero avere protezioni in modo che non vengano incluse più volte dall'applicazione troppo ambiziosa della regola 1 sopra.

Seguo anche le linee guida di:

  1. Includere prima le intestazioni di sistema (stdio.h, ecc.) Con una linea di divisione.
  2. Raggruppali logicamente.

In altre parole:

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

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

Tuttavia, essendo linee guida, è una cosa soggettiva. D'altro canto, le regole vengono applicate rigidamente, fino al punto di fornire file di intestazione "wrapper" con protezioni include e include raggruppate se qualche odioso sviluppatore di terze parti non si abbona alla mia visione :-)


6
+1 "Tutte le intestazioni (e in effetti qualsiasi file di origine) dovrebbero includere ciò di cui hanno bisogno. Non dovrebbero fare affidamento sui propri utenti, inclusi gli elementi." Eppure così tante persone fanno affidamento su questo comportamento di inclusione implicita, ad esempio, con NULL e non includono <cstddef>. È così fastidioso quando si tenta di eseguire il porting di questo codice e si ottengono errori di compilazione su NULL (un motivo per cui uso solo 0 ora).
puzzolente472

20
Perché includi prima le intestazioni di sistema? Sarebbe meglio l'altro perché in giro a causa della tua prima regola.
jhasse,

Se usi le macro di test delle funzionalità, molto probabilmente la tua prima inclusione NON deve essere un'intestazione di libreria standard. Pertanto, per generalità, direi che la politica "prima locale, poi globale" è la cosa migliore.
Hmijail piange le dimissioni il

1
Per quanto riguarda il tuo primo suggerimento di "non fare affidamento sugli utenti", che dire delle dichiarazioni forward nel file di intestazione che non richiedono il file di intestazione da includere? Dovremmo ancora includere il file di intestazione perché le dichiarazioni in avanti mettono l'onere sull'utente del file di intestazione per includere i file appropriati.
Zoso,

22

Per aggiungere il mio mattone al muro.

  1. Ogni intestazione deve essere autosufficiente, che può essere testata solo se inclusa prima almeno una volta
  2. Non si deve modificare erroneamente il significato di un'intestazione di terze parti introducendo simboli (macro, tipi, ecc.)

Quindi di solito vado così:

// 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"

Ogni gruppo separato da una riga vuota da quella successiva:

  • Intestazione corrispondente prima a questo file cpp (controllo di integrità)
  • Intestazioni di sistema
  • Intestazioni di terze parti, organizzate per ordine di dipendenza
  • Intestazioni di progetto
  • Progetto intestazioni private

Inoltre, a parte le intestazioni di sistema, ogni file si trova in una cartella con il nome del suo spazio dei nomi, solo perché è più facile rintracciarli in questo modo.


2
In modo che altri file di intestazione non siano interessati da loro. Sia per ciò che definiscono quelle intestazioni di sistema (sia X include e Windows include sono cattive su #definequello che incasina altro codice) e per prevenire dipendenze implicite. Ad esempio, se il nostro file di intestazione della base di codice foo.hdipende davvero, <map>ma dovunque fosse usato nei .ccfile, <map>è già stato incluso, probabilmente non lo noteremmo. Fino a quando qualcuno ha cercato di includere foo.hsenza prima includere <map>. E poi sarebbero infastiditi.

@ 0A0D: il secondo problema non è un problema nell'ordine qui, perché ognuno .hha almeno uno .cppche lo include per primo (in effetti, nel mio codice personale il test unit associato lo include per primo, e il codice sorgente lo include nel suo legittimo gruppo ). Per quanto riguarda il fatto di non essere influenzato, se una qualsiasi delle intestazioni include, <map>allora tutte le intestazioni incluse in seguito sono influenzate comunque, quindi mi sembra una battaglia persa.
Matthieu M.,

1
Naturalmente, ecco perché vado regolarmente in giro e correggo il codice più vecchio (o anche il codice più recente) che richiede un'inclusione non necessaria perché fa solo aumentare i tempi di costruzione.

@MatthieuM. Mi piacerebbe conoscere la logica alla base del tuo punto uno cioè Header corresponding to this cpp file first (sanity check). C'è qualcosa di particolare se #include "myproject/example.h"viene spostato alla fine di tutte le inclusioni?
MNS,

1
@MNS: un'intestazione dovrebbe essere indipendente, ovvero non dovrebbe essere necessario includere qualsiasi altra intestazione prima di essa. È responsabilità dell'utente, in qualità di autore dell'intestazione, accertarsi di ciò e il modo migliore per farlo è disporre di un file di origine in cui questa intestazione sia inclusa per prima. L'uso del file di origine corrispondente al file di intestazione è semplice, un'altra buona scelta è quella di utilizzare il file di origine dell'unità test corrispondente al file di intestazione ma è meno universale (potrebbero non esserci test di unità).
Matthieu M.,

16

Io raccomando:

  1. L'intestazione per il modulo .cc che stai costruendo. (Aiuta a garantire che ogni intestazione del progetto non abbia dipendenze implicite da altre intestazioni del progetto.)
  2. File di sistema C.
  3. File di sistema C ++.
  4. Piattaforma / sistema operativo / altri file di intestazione (ad esempio win32, gtk, openGL).
  5. Altri file di intestazione dal tuo progetto.

E, naturalmente, l'ordine alfabetico all'interno di ogni sezione, ove possibile.

Utilizzare sempre dichiarazioni anticipate per evitare messaggi non necessari #includenei file di intestazione.


+1, ma perché in ordine alfabetico? Sembra qualcosa che può farti sentire meglio, ma non ha alcun vantaggio pratico.
Ben

9
Alfabetico è un ordinamento arbitrario, ma facile. Non devi fare alfabetico, ma devi scegliere alcuni ordini in modo che tutti lo facciano in modo coerente. Ho scoperto che aiuta a evitare duplicati e facilita le fusioni. E se usi un testo sublime, F5 li ordinerà per te.
i_am_jorf,

14

Sono abbastanza sicuro che questa non sia una pratica consigliata in qualsiasi parte del mondo sano, ma mi piace allineare il sistema in base alla lunghezza del nome del file, ordinato lessicamente nella stessa lunghezza. Così:

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

Penso che sia una buona idea includere le proprie intestazioni prima di altre persone, per evitare la vergogna della dipendenza dall'ordine di inclusione.


3
Mi piace ordinare le mie intestazioni usando una chiave composta dalla seconda, terza, quindi prima lettera in quell'ordine :-) Quindi vettore, set, algoritmo, funzionale per il tuo esempio.
paxdiablo,

@paxdiablo, grazie per il suggerimento. Sto pensando di usarlo, ma sono preoccupato che potrebbe finire per lasciare instabile il mucchio di nomi di file e probabilmente ribaltarsi. Chissà cosa potrebbe essere incluso se ciò dovesse accadere, forse anche windows.h.
clstrfsck,

40
Ordinati per lunghezza ? Follia!
James McNellis

1
+1 per il primo. In realtà ha senso, se è necessario individuare visivamente le intestazioni all'interno di un file con gli occhi, è molto meglio di quello alfabetico.
Kugel,

6

Questo non è soggettivo. Assicurati che le intestazioni non facciano affidamento sull'essere #includein un ordine specifico. Puoi essere sicuro che non importa quale ordine includi le intestazioni STL o Boost.


1
Non stavo assumendo dipendenze implicite
Anycorn

Sì, ma il compilatore non può fare questo presupposto, quindi #include <A>, <B> non è mai uguale a #include <B>, <A> fino a quando non sono stati compilati.
Mikhail,

4

In primo luogo includere l'intestazione corrispondente al .cpp ... in altre parole, source1.cppdovrebbe includere source1.hprima di includere qualsiasi altra cosa. L'unica eccezione che mi viene in mente è quando si utilizza MSVC con intestazioni precompilate, nel qual caso, si è costretti a includere stdafx.hprima di ogni altra cosa.

Ragionamento: l' inclusione del source1.hprecedente prima di qualsiasi altro file garantisce che possa essere autonomo senza dipendenze. Se source1.hassume una dipendenza in una data successiva, il compilatore ti avviserà immediatamente di aggiungere le dichiarazioni forward richieste source1.h. Questo a sua volta garantisce che le intestazioni possano essere incluse in qualsiasi ordine dai loro dipendenti.

Esempio:

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

Utenti MSVC: consiglio vivamente di utilizzare intestazioni precompilate. Quindi, sposta tutte le #includedirettive per le intestazioni standard (e altre intestazioni che non cambieranno mai) in stdafx.h.


2

Includi dal più specifico al meno specifico, iniziando con il corrispondente .hpp per il .cpp, se ne esiste uno. In questo modo, verranno rivelate eventuali dipendenze nascoste nei file di intestazione che non sono autosufficienti.

Ciò è complicato dall'uso di intestazioni precompilate. Un modo per aggirare questo è, senza rendere specifico il compilatore del progetto, è utilizzare una delle intestazioni del progetto poiché l'intestazione precompilata include il file.


1

È una domanda difficile nel mondo C / C ++, con così tanti elementi oltre lo standard.

Penso che l'ordine dei file di intestazione non sia un problema serio finché si compila, come ha detto squelart.

Le mie idee sono: se non vi è alcun conflitto di simboli in tutte quelle intestazioni, qualsiasi ordine è OK e il problema di dipendenza dell'intestazione può essere risolto in seguito aggiungendo #include righe al .h difettoso.

La vera seccatura sorge quando alcune intestazioni cambiano la sua azione (controllando le condizioni #if) in base a quali intestazioni sono sopra.

Ad esempio, in stddef.h in VS2005, c'è:

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

Ora il problema: se ho un'intestazione personalizzata ("custom.h") che deve essere utilizzata con molti compilatori, inclusi alcuni più vecchi che non forniscono offsetofnelle loro intestazioni di sistema, dovrei scrivere nella mia intestazione:

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

E assicurati di dire all'utente di #include "custom.h" dopo tutte le intestazioni di sistema, altrimenti la riga offsetofin in stddef.h affermerà un errore di ridefinizione delle macro.

Preghiamo di non incontrare più questi casi nella nostra carriera.

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.