Scrittura della definizione della funzione nei file di intestazione in C ++
-
19-08-2019 - |
Domanda
Ho una classe che ha molte piccole funzioni. Per piccole funzioni intendo funzioni che non eseguono alcuna elaborazione ma restituiscono solo un valore letterale. Qualcosa del tipo:
string Foo::method() const{
return "A";
}
Ho creato un file header " Foo.h " e file sorgente " Foo.cpp " ;. Ma poiché la funzione è molto piccola, sto pensando di inserirla nel file di intestazione stesso. Ho le seguenti domande:
- Vi sono problemi di prestazioni o altri problemi se inserisco queste definizioni delle funzioni nel file di intestazione? Avrò molte funzioni come questa.
- La mia comprensione è al termine della compilazione, il compilatore espanderà il file di intestazione e lo posizionerà dove è incluso. È corretto?
Soluzione
Se la funzione è piccola (la possibilità di cambiarla spesso è bassa) e se la funzione può essere inserita nell'intestazione senza includere miriadi di altre intestazioni (poiché la funzione dipende da esse), è perfettamente valida fare così. Se li dichiarate in linea all'esterno, il compilatore è tenuto a dargli lo stesso indirizzo per ogni unità di compilazione:
headera.h :
inline string method() {
return something;
}
Le funzioni membro sono inline implicite a condizione che siano definite all'interno della loro classe. Lo stesso vale per loro: se possono essere inseriti nell'intestazione senza problemi, puoi davvero farlo.
Poiché il codice della funzione è inserito nell'intestazione e visibile, il compilatore è in grado di incorporare le chiamate verso di loro, cioè inserendo il codice della funzione direttamente nel sito di chiamata (non tanto perché lo hai messo in linea prima di esso , ma di più perché il compilatore decide in questo modo, però. Mettere inline è solo un suggerimento per il compilatore). Ciò può comportare un miglioramento delle prestazioni, perché il compilatore ora vede dove gli argomenti corrispondono alle variabili locali alla funzione e dove l'argomento non si alias l'altro - e, ultimo ma non meno importante, l'allocazione del frame della funzione non è più necessaria.
La mia comprensione è al termine della compilazione, il compilatore espande il file di intestazione e lo posiziona dove è incluso. È corretto?
Sì, è corretto. La funzione sarà definita in ogni punto in cui includi la sua intestazione. Il compilatore si preoccuperà di inserire solo una sua istanza nel programma risultante, eliminando le altre.
Altri suggerimenti
A seconda del compilatore e delle sue impostazioni, può eseguire una delle seguenti operazioni:
- Potrebbe ignorare la parola chiave incorporata (esso è solo un suggerimento per il compilatore, non un comando) e generare autonomo funzioni. Potrebbe farlo se il tuo le funzioni superano un compilatore dipendente soglia di complessità. per esempio. troppi loop nidificati.
- Potrebbe decidere rispetto al tuo stand-alone la funzione è un buon candidato per espansione in linea.
In molti casi, il compilatore è in una posizione molto migliore per determinare se una funzione deve essere incorporata rispetto a te, quindi non ha senso ripensarla. Mi piace usare la linea implicita quando una classe ha molte piccole funzioni solo perché è conveniente avere l'implementazione proprio lì nella classe. Questo non funziona così bene per funzioni più grandi.
L'altra cosa da tenere a mente è che se si esporta una classe in una libreria DLL / condivisa (non è una buona idea IMHO, ma le persone lo fanno comunque) è necessario fare molta attenzione con le funzioni incorporate. Se il compilatore che ha creato la DLL decide che una funzione dovrebbe essere integrata, hai un paio di potenziali problemi:
- Il compilatore che crea il programma l'utilizzo della DLL potrebbe decidere di non farlo incorporare la funzione così sarà generare un riferimento simbolo a funzione che non esiste e il La DLL non verrà caricata.
- Se si aggiorna la DLL e si modifica il funzione incorporata, il programma client utilizzerà ancora la versione precedente di quella funzione poiché la funzione è stato inserito nel codice client.
Ci sarà un aumento delle prestazioni perché l'implementazione nei file di intestazione è implicitamente incorporata. Come hai detto, le tue funzioni sono piccole, le operazioni in linea saranno molto utili per il tuo IMHO.
Anche quello che dici sul compilatore è vero. Non c'è differenza per il compilatore & # 8212; diverso dall'inline & # 8212; tra il codice nel file di intestazione o il file .cpp
.
-
Se le tue funzioni sono così semplici, rendile in linea e dovrai comunque inserirle nel file di intestazione. A parte questo, tutte le convenzioni sono proprio questo: convenzioni.
-
Sì, il compilatore espande il file di intestazione dove incontra le istruzioni #include.
Dipende dagli standard di codifica che si applicano nel tuo caso ma:
Le piccole funzioni senza loop e nient'altro dovrebbero essere definite per migliorare le prestazioni (ma un codice leggermente più grande - importante per alcune applicazioni vincolate o incorporate).
Se hai il corpo della funzione nell'intestazione, la avrai automaticamente in linea (d) (che è una buona cosa quando si tratta di velocità).
Prima che il file oggetto venga creato dal compilatore, viene chiamato il preprocessore (opzione -E per gcc) e il risultato viene inviato al compilatore che crea l'oggetto fuori codice.
Quindi la risposta più breve è:
- Dichiarare le funzioni nell'intestazione è buono per la velocità (ma non per lo spazio) -
C ++ non ti lamenterà se lo fai, ma in generale, non dovresti & # 8217; t.
quando #includi un file, l'intero contenuto del file incluso viene inserito nel punto di inclusione. Ciò significa che qualsiasi definizione inserita nell'intestazione viene copiata in ogni file che include quell'intestazione.
Per i piccoli progetti, questo non è probabilmente un grosso problema. Ma per i progetti più grandi, ciò può richiedere molto più tempo per la compilazione (poiché lo stesso codice viene ricompilato ogni volta che viene incontrato) e potrebbe gonfiare in modo significativo le dimensioni dell'eseguibile. Se si modifica una definizione in un file di codice, è necessario ricompilare solo quel file .cpp. Se si modifica una definizione in un file di intestazione, tutti i file di codice che includono l'intestazione devono essere ricompilati. Una piccola modifica può farti ricompilare l'intero progetto!
A volte vengono fatte eccezioni per funzioni banali che è improbabile che cambino (ad es. dove la definizione della funzione è una riga).
Fonte: http://archive.li/ACYlo (versione precedente del capitolo 1.9 su learncpp.com )
Dovresti usare le funzioni incorporate. Leggi questo Funzioni in linea per una migliore comprensione e i compromessi coinvolti .