Organizzazione dei file di intestazione della class C ++

Quali sono le linee guida per la codifica e l’organizzazione dei file in C ++ che suggerisci per le persone che hanno a che fare con molte classi interdipendenti distribuite su più file sorgente e di intestazione?

Ho questa situazione nel mio progetto e risolvere gli errori relativi alla definizione della class che attraversano diversi file di intestazione è diventato piuttosto un mal di testa.

Alcune linee guida generali:

  • Accoppia le tue interfacce con le implementazioni. Se hai foo.cxx , è meglio che tutto ciò che è definito in là sia dichiarato in foo.h
  • Assicurarsi che ogni file di intestazione includa tutte le altre intestazioni o dichiarazioni avanzate necessarie per la compilazione indipendente.
  • Resisti alla tentazione di creare un’intestazione “tutto”. Sono sempre problemi lungo la strada.
  • Metti un insieme di funzionalità correlate (e interdipendenti) in un singolo file. Java e altri ambienti incoraggiano una class per file. Con C ++, si desidera spesso un set di classi per file. Dipende dalla struttura del tuo codice.
  • Preferisci la dichiarazione in avanti su #include s quando ansible. Ciò consente di interrompere le dipendenze dell’intestazione ciclica. In sostanza, per le dipendenze cicliche su file separati, si desidera un grafico di dipendenza file simile a questo:
    • A.cxx richiede Ah e Bh
    • B.cxx richiede Ah e Bh
    • Ah richiede Bh
    • Bh è indipendente (e forward-dichiara le classi definite in Ah )

Se il tuo codice è destinato a essere una libreria consumata da altri sviluppatori, ci sono alcuni passaggi aggiuntivi che sono importanti da prendere:

  • Se necessario, utilizzare il concetto di “intestazioni private”. Ovvero, i file di intestazione richiesti da diversi file di origine, ma mai richiesti dall’interfaccia pubblica. Questo potrebbe essere un file con funzioni inline comuni, macro o costanti interne.
  • Separa la tua interfaccia pubblica dalla tua implementazione privata a livello di filesystem. Tendo ad usare include/ e src/ subdirectories nei miei progetti C o C ++, dove include/ ha tutte le mie intestazioni pubbliche e src/ ha tutte le mie fonti. e intestazioni private.

Consiglierei di trovare una copia del progetto di software C ++ su grande scala di John Lakos. È un libro piuttosto pesante, ma se sfogli alcune delle sue discussioni sull’architettura fisica, imparerai molto.

Controlla gli standard di codifica C e C ++ presso il NASA Goddard Space Flight Center . L’unica regola che ho notato nello standard C e che ho adottato nel mio codice è quella che impone la natura ‘standalone’ dei file di intestazione. Nel file di implementazione xxx.cpp per l’intestazione xxx.h, assicurarsi che xxx.h sia la prima intestazione inclusa. Se l’intestazione non è autonoma in qualsiasi momento, la compilazione fallirà. È una regola magnificamente semplice ed efficace.

L’unica volta che si fallisce è se si porta tra le macchine, e l’intestazione xxx.h include, per esempio, , ma richiede servizi che possono essere resi disponibili da un’intestazione sulla piattaforma originale (quindi include ), ma le funzioni non sono rese disponibili da sull’altra piattaforma (sono invece in def.h , ma non include ). Questo non è un difetto della regola, e il problema è più facilmente diagnosticato e risolto se si segue la regola.

Controlla la sezione del file di intestazione nella guida di stile di Google

La risposta di Tom è eccellente!

L’unica cosa che aggiungerei è di non aver mai “usato le dichiarazioni” nei file di intestazione. Dovrebbero essere consentiti solo nei file di implementazione, ad esempio foo.cpp .

La logica per questo è ben descritta nell’eccellente libro “Accelerated C ++” ( link Amazon – sanitized per script kiddie link nazis)

Un altro punto in aggiunta agli altri qui:

Non includere definizioni private in un file di inclusione. Ad esempio, qualsiasi definizione che viene utilizzata solo in xxx.cpp deve essere in xxx.cpp, non in xxx.h.

Sembra ovvio, ma lo vedo spesso.

Mi piacerebbe aggiungere una pratica molto buona (sia in C che in C ++) che è spesso abbandonata:

foo.c

 #include "foo.h" // always the first directive 

Qualsiasi altra intestazione necessaria dovrebbe seguire, quindi codice. Il punto è che hai quasi sempre bisogno di quell’intestazione per questa unità di compilazione e includerla come prima direttiva garantisce che l’intestazione rimane autosufficiente (se non lo è, ci saranno errori). Questo è particolarmente vero per le intestazioni pubbliche.

Se in qualsiasi momento hai bisogno di inserire qualcosa prima dell’inclusione di questa intestazione (eccetto i commenti ovviamente), allora probabilmente stai facendo qualcosa di sbagliato. A meno che tu non sappia davvero cosa stai facendo … il che porta a un’altra regola più cruciale => commenta i tuoi hack!