2009-03-05 14 views
31

In che ordine devono essere dichiarate le intestazioni in un file header/cpp? Ovviamente quelli che sono richiesti dalle intestazioni successive dovrebbero essere precedenti e gli header specifici di classe dovrebbero essere nel scope cpp non nell'intestazione dell'intestazione, ma esiste una convenzione di ordine/best practice?C++ Ordine di intestazione

risposta

59

In un file di intestazione è necessario includere TUTTE le intestazioni per renderlo compilabile. E non dimenticare di usare le dichiarazioni in avanti invece di alcune intestazioni.

In un file sorgente:

  • file di intestazione corrisposto
  • necessarie intestazioni progetto
  • 3rd librerie di parti intestazioni
  • librerie standard headers
  • intestazioni di sistema

In questo ordina di non perdere nessuno di voi r file di intestazione che si sono dimenticati di includere le librerie da soli.

+2

Penso che "le intestazioni di progetto necessarie" dovrebbero essere separate su due elementi: - intestazioni di progetto dalla stessa lib; - intestazioni da altre librerie di progetto (nell'ordine in cui le intestazioni delle librerie di progetto più comuni seguono di seguito rispetto ad alcune intestazioni di librerie di progetti speciali) – bayda

+2

Mi piace molto il tuo ordine. In questo modo le intestazioni non si basano su altre intestazioni per includere le loro dipendenze. – fmuecke

3

ho usato per ordinarli in ordine alfabetico (più facile da trovare)

+0

Ecco come fa Epic, nel loro stile Unreal Engine 4 doc. –

0

E 'una cosa di dipendenza e dipende in gran parte su ciò che si mette nelle nostre intestazioni. Un fatto è che puoi essere davvero famoso per questo e minimizzare per mantenere i tuoi include severi, ma alla fine ti imbatterai in uno scenario in cui vorresti usare le protezioni per l'inclusione.

#ifndef MY_HEADER_H 
#define MY_HEADER_H 
//... 
#endif 

Il problema non è che apparente in principio, ma come la complessità del software cresce così fa le dipendenze. Puoi fare bene e essere intelligente, ma i progetti C++ più grandi sono generalmente pieni di include. Puoi provare, ma puoi fare così tanto. Quindi sii diligente e pensa al tuo include, SÌ! Ma avrai sicuramente delle dipendenze cicliche ad un certo punto ed è per questo che hai bisogno di protezioni per l'inclusione.

+1

Non è lo stesso che chiamare "#pragma once"? – Konrad

+1

Sì (più o meno) lo stesso, ma #pragma una volta non è standard. –

+0

Per la registrazione #pragma una volta è un'estensione Microsoft. Se sto lavorando su un progetto multipiattaforma, lo uso e includo guardie perché #pragma una volta impedisce effettivamente l'analisi del file. –

20

Buona pratica: ogni file .h dovrebbe avere un .cpp che include quello .h prima di ogni altra cosa. Ciò dimostra che qualsiasi file .h può essere inserito per primo.

Anche se l'intestazione non richiede implementazione, si crea un file .cpp che include solo il file .h e nient'altro.

Questo significa che è possibile rispondere alla domanda in qualsiasi modo. Non importa in quale ordine li includi.

Per ulteriori suggerimenti, prova questo libro: Large-Scale C++ Software Design - è un peccato che sia così costoso, ma è praticamente una guida di sopravvivenza per il layout del codice sorgente C++.

+0

Lo faccio invece nei miei test unitari. – Ferruccio

+4

Un test più facile da automatizzare: basta compilare (-c) ogni file di intestazione (sì, g ++ -c blah.h) e assicurarsi che compili. È quindi possibile eliminare i file oggetto. –

+0

+1, c'è molto che non è stato detto nella risposta. Ottimo consiglio! –

1

Per i file .cpp, è necessario includere l'intestazione della classe o qualunque cosa si stia implementando per prima, in modo da individuare il caso in cui questa intestazione non è inclusa. Dopodiché, la maggior parte delle linee guida di codifica tendono ad includere prima le intestazioni di sistema, le intestazioni di progetto in secondo luogo, ad esempio lo Google C++ Style Guide.

0

Se un'intestazione ha bisogno di altre intestazioni, le include semplicemente nell'intestazione.

Provare a strutturare il codice in modo da passare puntatori o riferimenti e inoltrare dichiarare dove è possibile.

Nell'implementazione, l'intestazione che lo definisce deve essere elencata per prima (eccetto in Visual Studio se si utilizza pch, quindi stdafx andrebbe prima).

Generalmente li elenco come ho bisogno.

6

Nei file di intestazione, tendo a inserire prima intestazioni standard, quindi le intestazioni personali (entrambe le liste vengono ordinate alfabeticamente). Nei file di implementazione, inserisco prima l'intestazione corrispondente (se presente), quindi le intestazioni standard e altre intestazioni di dipendenza.

L'ordine è di scarsa importanza, tranne se si fa un grande uso di macro e #define; in tal caso, devi verificare che una macro che hai definito non sostituisca quella precedentemente inclusa (eccetto se è quello che vuoi, ovviamente).

Per quanto riguarda questa dichiarazione

coloro che sono richiesti dalle intestazioni successive dovrebbero essere precedenti

Un colpo di testa non dovrebbe fare affidamento su altre intestazioni di essere inclusi prima! Se richiede intestazioni, le include solo. guardie Header impediranno inclusione multipla:

#ifndef FOO_HEADER_H 
#define FOO_HEADER_H 
... 
#endif 

EDIT

Da quando ho scritto questa risposta, ho cambiato il mio modo di ordinare l'inclusione direttive nel mio codice. Ora, cerco sempre di mettere le intestazioni in ordine crescente di standardizzazione, quindi le intestazioni del mio progetto vengono prima, seguite da intestazioni di librerie di terze parti, seguite da intestazioni standard.

Per esempio, se uno dei miei file utilizza una libreria che ho scritto, Qt, Boost e la libreria standard, io ordinerà la include come segue:

//foo.cpp 
#include "foo.hpp" 

#include <my_library.hpp> 
// other headers related to my_library 

#include <QtCore/qalgorithms.h> 
// other Qt headers 

#include <boost/format.hpp> // Boost is arguably more standard than Qt 
// other boost headers 

#include <algorithms> 
// other standard algorithms 

Il motivo per cui lo faccio è quello di rilevare mancano le dipendenze nelle intestazioni personali: supponiamo ad esempio che my_library.hpp usi std::copy, ma non includa <algorithm>. Se lo includo dopo lo <algorithm> in foo.cpp, questa dipendenza mancante passerà inosservata. Al contrario, con l'ordine che ho appena presentato, il compilatore si lamenterà che non è stato dichiarato std::copy, consentendomi di correggere my_library.hpp.

In ogni gruppo di "biblioteca", cerco di mantenere le istruzioni di inclusione ordinate alfabeticamente, per trovarle più facilmente.

Su un sidenote, una buona pratica è anche quella di limitare al massimo la dipendenza tra i file di intestazione. I file dovrebbero includere il minimo di intestazioni possibili, in particolare il file di intestazioni. Infatti, più intestazioni includi, più codice deve essere ricompilato quando qualcosa cambia. Un buon modo per limitare queste dipendenze è utilizzare la dichiarazione di inoltro, che è molto spesso sufficiente nei file di intestazione (vedere When can I use a forward declaration?).

+0

+1 per protezioni inclusione/intestazione. – polarise

-1

ho trovato la seguente convenzione più utili:

modulo.cpp:

// this is the header used to trigger inclusion of precompiled headers 
#include <precompiled.h> 
// this ensures that anything that includes "module.h" works 
#include "module.h" 
// other headers, usually system headers, the project 

L'importante è inserire l'intestazione del modulo come prima intestazione non precompilata. Ciò garantisce che "module.h" non abbia dipendenze impreviste.

Se stai lavorando su un progetto di grandi dimensioni con tempi di accesso al disco lento, ho visto questo stile usato per abbassare i tempi di compilazione:

module.cpp:

// this is the header used to trigger inclusion of precompiled headers 
#include <precompiled.h> 
// this ensures that anything that includes "module.h" works 
#include "module.h" 
// other headers, usually system headers, the project 
#if !defined _OTHER_MODULE_GUARD_ 
#include "other_module.h" 
#endif 

#if !defined _ANOTHER_MODULE_GUARD_ 
#include "another_module.h" 
#endif 

E 'un po' prolisso ma fa risparmiare sulla ricerca del disco poiché l'intestazione non verrà cercata/aperta se è già stata inclusa. Senza il controllo di guardia, il compilatore cercherà e aprirà il file di intestazione, analizzerà l'intero file per terminare lo #ifdef dell'intero file.

2

Il "come" non è ovvio, ma il "cosa" è. Il tuo obiettivo è di assicurarti che l'ordine in cui includi i file header non sia mai importante (e intendo "MAI!").

Un buon aiuto è verificare se i file di intestazione vengono compilati quando si creano file cpp (uno per ciascun file di intestazione) che include solo uno di essi.

5

Google C++ Style Guide, Names and Order of Includes:

A dir/foo.cc, il cui scopo principale è quello di attuare o testare la roba in dir2/foo2.h, ordinare il include quanto segue:

  • dir2/foo2.h (posizione preferita - vedi i dettagli di seguito).
  • File di sistema C.
  • File di sistema C++.
  • File .h di altre librerie.
  • I file del progetto .h.
+5

sì, ma la guida in stile ggl ha alcune cose sgradevoli :) –

Problemi correlati