Domanda

Quale ordine devono essere dichiarate le intestazioni in un file header / cpp? Ovviamente quelli richiesti dalle intestazioni successive dovrebbero essere precedenti e le intestazioni specifiche per classe dovrebbero rientrare nell'ambito cpp e non in quello dell'intestazione, ma esiste una convenzione di ordine stabilito / best practice?

È stato utile?

Soluzione

In un file di intestazione devi includere TUTTE le intestazioni per renderlo compilabile. E non dimenticare di utilizzare le dichiarazioni forward invece di alcune intestazioni.

In un file sorgente:

  • file di intestazione corrispondente
  • intestazioni di progetto necessarie
  • intestazioni di librerie di terze parti
  • intestazioni delle librerie standard
  • intestazioni di sistema

In questo ordine non ti perderai nessuno dei tuoi file di intestazione che hanno dimenticato di includere le loro librerie.

Altri suggerimenti

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

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

Questo significa che puoi rispondere alla tua domanda come preferisci. Non importa in quale ordine li includi.

Per ulteriori suggerimenti, prova questo libro: Design del software C ++ su larga scala - è un peccato, è così costoso, ma è praticamente una guida di sopravvivenza per il layout del codice sorgente C ++.

Nei file di intestazione, tendo a mettere prima le intestazioni standard, quindi le mie intestazioni (entrambe le liste sono ordinate in ordine alfabetico). Nei file di implementazione, inserisco prima l'intestazione corrispondente (se presente), quindi le intestazioni standard e altre intestazioni di dipendenza.

L'ordine ha poca importanza, tranne se si fa un grande uso di macro e #define; in tal caso, è necessario verificare che una macro definita non sostituisca una precedentemente inclusa (tranne se è quello che si desidera, ovviamente).

Riguardo a questa affermazione

  

quelli richiesti dalle intestazioni successive dovrebbero essere precedenti

Un'intestazione non dovrebbe fare affidamento sul fatto che altre intestazioni vengano incluse prima! Se richiede intestazioni, le include solo. Le protezioni delle intestazioni impediranno l'inclusione multipla:

#ifndef FOO_HEADER_H
#define FOO_HEADER_H
...
#endif

Modifica

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

Ad esempio, se uno dei miei file utilizza una libreria che ho scritto, Qt, Boost e la libreria standard, ordinerò le inclusioni 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 è rilevare dipendenze mancanti nelle mie intestazioni: supponiamo ad esempio che my_library.hpp usi std::copy, ma non includa <algorithm>. Se lo includo dopo foo.cpp in <=>, questa dipendenza mancante passerà inosservata. Al contrario, con l'ordine che ho appena presentato, il compilatore si lamenterà che <=> non è stato dichiarato, permettendomi di correggere <=>.

In ogni " libreria " gruppo, cerco di mantenere le direttive di inclusione ordinate in ordine alfabetico, 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 minor numero possibile di intestazioni, in particolare il file delle intestazioni. In effetti, più intestazioni includi, più codice deve essere ricompilato quando qualcosa cambia. Un buon modo per limitare queste dipendenze è utilizzare la dichiarazione diretta, che è spesso sufficiente nei file di intestazione (vedere Quando posso usare una dichiarazione diretta ? ).

Guida allo stile, nomi e ordini di inclusione di Google C ++ :

  

In dir / foo.cc, il cui scopo principale è implementare o testare le cose in dir2 / foo2.h, ordina le tue inclusioni come segue:

     
      
  • dir2 / foo2.h (posizione preferita & # 8212; vedere i dettagli di seguito).
  •   
  • file di sistema C.
  •   
  • File di sistema C ++.
  •   
  • File .h di altre librerie.
  •   
  • I file .h del tuo progetto.
  •   

Li ordinavo in ordine alfabetico (più facile da trovare)

Il " come " non è ovvio, ma il " what " è. Il tuo obiettivo è assicurarti che l'ordine in cui includi i file di intestazione non abbia mai importanza (e intendo & Quot; MAI!!!!! Quot;).

Un valido aiuto è verificare se i file di intestazione vengono compilati quando si creano file cpp (uno per ogni file di intestazione) che ne includono solo uno.

Per i file .cpp, dovresti includere l'intestazione della classe o qualunque cosa tu stia implementando per prima, quindi cogli il caso in cui questa intestazione manchi alcune include. Dopodiché, la maggior parte delle linee guida per la codifica tendono a includere prima le intestazioni di sistema, le intestazioni di progetto in secondo luogo, ad esempio Guida allo stile di Google C ++ .

È una cosa di dipendenza e dipende in gran parte da ciò che metti nelle nostre intestazioni. Un dato di fatto è che puoi essere davvero noto su questo e minimizzare per mantenere rigide le tue inclusioni ma alla fine ti imbatterai in uno scenario in cui vorrai usare le protezioni per l'inclusione.

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

Il problema non è così evidente all'inizio, ma con l'aumentare della complessità del software aumentano anche le dipendenze. Puoi fare bene ed essere furbo, ma i progetti C ++ più grandi sono generalmente pieni di inclusioni. Puoi provare, ma puoi fare solo così tanto. Quindi sii diligente e pensa alle tue inclusioni, SÌ! Ma avrai sicuramente dipendenze cicliche ad un certo punto ed è per questo che hai bisogno di protezioni per l'inclusione.

Se un'intestazione necessita di altre intestazioni, le include solo in quell'intestazione.

Prova a strutturare il tuo codice in modo da passare puntatori o riferimenti e inoltrare la dichiarazione dove puoi.

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

In genere li elenco di cui ho bisogno.

Ho trovato la seguente convenzione la più utile:

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

L'importante è mettere l'intestazione del modulo come prima intestazione non precompilata. Questo assicura & Quot; module.h & Quot; non ha dipendenze impreviste.

Se stai lavorando su un grande progetto con tempi di accesso al disco lenti, ho visto questo stile usato per ridurre i tempi di costruzione:

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 

È un po 'prolisso ma salva sulla ricerca del disco poiché l'intestazione non verrà cercata / aperta se è già stata inclusa. Senza il controllo di protezione, il compilatore cercherà e aprirà il file di intestazione, analizzerà l'intero file per finire #ifdef eseguendo l'intero file.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top