2011-01-21 5 views
24

Ciao e grazie per la lettura. questo potrebbe essere solo che rientrano nella categoria delle 'preferenze personali' ma in ogni caso qui andiamo ...Definizione del costruttore nel file di intestazione VS file di implementazione (.cpp)

posso definire il corpo di un costruttore della classe nella classe .h file o nel file di implementazione cpp. Questi due stili sono probabilmente identici per quanto riguarda il compilatore in un progetto specifico (progetto per me significa dll). Lo stesso vale per qualsiasi funzione membro realmente: possono essere definiti nel file di intestazione o semplicemente dichiarati lì e quindi definiti nel file cpp.

TUTTAVIA ...

ho scoperto che se ho bisogno di includere file di intestazione tale classe (s) in diversi progetti (il che significa che in ultima analisi, il codice che utilizza il file di intestazione finisce in un diverso dll) quindi avere l'implementazione effettiva nel file di intestazione causa alcuni grattacapi alla compilazione (non al collegamento ... non arrivo nemmeno a quel punto). Perché? Beh, non voglio andare troppo in dettaglio ma il compilatore cerca ovviamente di risolvere tutte le funzioni che potrebbero essere definite in altri file header ... costringendo il povero sviluppatore a iniziare a tirare in vari file header eetc ...

LONGSTORY breve:

Non è sempre meglio mantenere i file di intestazione liberi da qualsiasi implementazione e usarli per le "dichiarazioni"? Ciò renderebbe più facile includerli in più di un progetto senza dover portare con sé un sacco di spazzatura in più.

Qual è la vostra opinione? Grazie!

risposta

21

Mantenere le intestazioni prive di implementazioni, a meno che non si desideri che le implementazioni siano in linea (ad esempio getter/setter banali). E a meno che non siano modelli, ovviamente.

Non vedo alcun motivo per fare un'eccezione per i costruttori. Inseriscili nel file .cpp.

+1

Esistono anche casi in cui è possibile inserire un'implementazione nell'intestazione. Supponiamo che se due file diversi entrambi siano necessari per includersi l'un l'altro, ciò non può essere fatto nell'intestazione, deve essere fatto nel cpp. –

+0

Intendi "non può", non "può". Altrimenti, un buon punto. – Thomas

+2

L'eccezione "banale" può essere applicata facilmente a un costruttore banale. Non c'è motivo di trattarli in modo diverso o di non averli anch'essi in linea. –

17

Un punto importante da notare è che se una funzione membro è definita all'interno di un file di intestazione, deve trovarsi all'interno del corpo della classe oppure deve essere contrassegnata esplicitamente come inline. In altre parole, è semplicemente sbagliato a fare questo in un file di intestazione:

class A { 
    public: 
    A(); 
}; 

A::A() { 
    // constructor body 
} 

Il motivo è sbagliato è perché renderà il compilatore includono la definizione di ogni unità di compilazione, mentre è ovvio che qualsiasi funzione deve essere definito solo una volta. Qui ci sono modi corretti per fare la stessa cosa:

class A { 
    public: 
    inline A(); 
}; 

inline A::A() { 
    // constructor body 
} 

Oppure:

class A { 
    public: 
    inline A() { // inline isn't required here, but it's a good style 
    // constructor body 
    } 
}; 

In entrambi i casi il costruttore è in linea. L'unico modo corretto per renderlo una normale funzione out-of-line sarebbe definirlo nel file di implementazione, non nell'intestazione. Questa è la differenza più importante tra questi due approcci.

Ora, vale la pena notare che l'inlining è un'ottimizzazione. E come sempre con le ottimizzazioni, è meglio evitarlo fino a quando non sarà necessario.Tra gli altri problemi che l'inlining può portare, c'è il problema di compatibilità: se si modifica il corpo di una funzione che non è in linea, è necessario solo ricompilare l'unità dove è definita, e tutti iniziano a utilizzare immediatamente la nuova implementazione. Con le funzioni integrate, è necessario ricompilare ogni unità che include l'intestazione pertinente, il che può essere un problema, soprattutto se l'intestazione viene utilizzata in diversi progetti da persone diverse.

In altre parole, utilizzare le definizioni fuori linea regolari laddove possibile finché non viene dimostrato mediante il profiling che una particolare chiamata di funzione è un collo di bottiglia delle prestazioni. L'unica ragionevole eccezione a questa regola sono gli insignificanti ei getter, e anche con loro è meglio stare attenti - un giorno potrebbero diventare non banali e significherà un sacco di ricompense e rottura della compatibilità.

1

Un'altra nota da considerare: eventuali modifiche a un file di intestazione richiedono la ricostruzione di tutti i file che includono tale file di intestazione. La maggior parte dei sistemi di compilazione ricostruirà i file sorgente (* .cpp/.cc) che dipendono dal file di intestazione modificato.

Se si modifica un metodo di una classe definita in un file di intestazione, verranno ricostruiti tutti i file di origine, incluso il file di intestazione. Se si modifica un metodo in un file di origine, viene ricostruito solo il file di origine. Questo potrebbe essere un problema per progetti di dimensioni medio-grandi.

Per semplificare il processo di compilazione, è necessario definire la maggior parte dei metodi di una classe in un file di origine. Piccoli metodi e altri candidati per l'inlining dovrebbero essere definiti nel file di intestazione.