Il fatto è che, in C ++, questo è un po 'più complicato dell'organizzazione dell'intestazione / sorgente C.
Cosa vede il compilatore?
Il compilatore vede un grande file sorgente (.cpp) con le sue intestazioni correttamente incluse. Il file sorgente è l'unità di compilazione che verrà compilata in un file oggetto.
Allora, perché sono necessarie le intestazioni?
Perché un'unità di compilazione potrebbe richiedere informazioni su un'implementazione in un'altra unità di compilazione. Quindi si può scrivere, ad esempio, l'implementazione di una funzione in una sorgente e scrivere la dichiarazione di questa funzione in un'altra sorgente che necessita di utilizzarla.
In questo caso, ci sono due copie delle stesse informazioni. Che è male ...
La soluzione è condividere alcuni dettagli. Mentre l'implementazione dovrebbe rimanere nel Source, potrebbe essere necessario condividere la dichiarazione di simboli condivisi, come funzioni, o la definizione di strutture, classi, enumerazioni, ecc.
Le intestazioni vengono utilizzate per inserire quei dettagli condivisi.
Spostare nell'intestazione le dichiarazioni di ciò che deve essere condiviso tra più fonti
Niente di più?
In C ++, ci sono altre cose che potrebbero essere inserite nell'intestazione perché, anch'esse, devono essere condivise:
- codice inline
- modelli
- costanti (di solito quelle che vuoi usare all'interno degli interruttori ...)
Passa all'intestazione TUTTO ciò che deve essere condiviso, comprese le implementazioni condivise
Significa quindi che potrebbero esserci fonti all'interno delle intestazioni?
Sì. In effetti, ci sono molte cose diverse che potrebbero essere all'interno di un "header" (cioè condiviso tra sorgenti).
- Dichiarazioni future
- dichiarazioni / definizione di funzioni / strutture / classi / modelli
- implementazione di codice inline e basato su modelli
Diventa complicato e in alcuni casi (dipendenze circolari tra simboli), impossibile tenerlo in un'intestazione.
Le intestazioni possono essere suddivise in tre parti
Ciò significa che, in un caso estremo, potresti avere:
- un'intestazione di dichiarazione anticipata
- un'intestazione di dichiarazione / definizione
- un'intestazione di implementazione
- una fonte di implementazione
Immaginiamo di avere un MyObject basato su modelli. Potremmo avere:
// - - - - MyObject_forward.hpp - - - -
// This header is included by the code which need to know MyObject
// does exist, but nothing more.
template<typename T>
class MyObject ;
.
// - - - - MyObject_declaration.hpp - - - -
// This header is included by the code which need to know how
// MyObject is defined, but nothing more.
#include <MyObject_forward.hpp>
template<typename T>
class MyObject
{
public :
MyObject() ;
// Etc.
} ;
void doSomething() ;
.
// - - - - MyObject_implementation.hpp - - - -
// This header is included by the code which need to see
// the implementation of the methods/functions of MyObject,
// but nothing more.
#include <MyObject_declaration.hpp>
template<typename T>
MyObject<T>::MyObject()
{
doSomething() ;
}
// etc.
.
// - - - - MyObject_source.cpp - - - -
// This source will have implementation that does not need to
// be shared, which, for templated code, usually means nothing...
#include <MyObject_implementation.hpp>
void doSomething()
{
// etc.
} ;
// etc.
Wow!
Nella "vita reale", di solito è meno complicato. La maggior parte del codice avrà solo una semplice organizzazione di intestazione / sorgente, con un po 'di codice inline nel sorgente.
Ma in altri casi (oggetti modellati che si conoscono a vicenda), dovevo avere per ogni oggetto dichiarazioni separate e intestazioni di implementazione, con una fonte vuota che includesse quelle intestazioni solo per aiutarmi a vedere alcuni errori di compilazione.
Un altro motivo per suddividere le intestazioni in intestazioni separate potrebbe essere accelerare la compilazione, limitando la quantità di simboli analizzati allo stretto necessario ed evitando la ricompilazione non necessaria di una fonte che si preoccupa solo della dichiarazione in avanti quando l'implementazione di un metodo inline cambia.
Conclusione
È necessario rendere l'organizzazione del codice il più semplice possibile e il più modulare possibile. Metti quanto più possibile nel file sorgente. Esponi nelle intestazioni solo ciò che deve essere condiviso.
Ma il giorno in cui avrai dipendenze circolari tra oggetti modellati, non sorprenderti se la tua organizzazione del codice diventa un po 'più "interessante" della semplice organizzazione di intestazione / origine ...
^ _ ^