Domanda

Ho appena iniziato l'apprendimento del Qt, utilizzando i loro tutorial.Attualmente sono al tutorial 7, dove abbiamo fatto una nuova LCDRange classe.L'attuazione di LCDRange (l' .file cpp), usa le Qt QSlider classe, in modo che in .file cpp è

#include <QSlider>

ma l'intestazione è un avanti dichiarazione:

class QSlider;

Secondo Qt,

Questo è un altro classico trucco, ma uno che è molto meno usato spesso.Perché non abbiamo bisogno di QSlider nell'interfaccia della classe, solo in fase di attuazione, usiamo avanti e la dichiarazione della classe nel file di intestazione e includere il file di intestazione per QSlider nel .file cpp.

Questo rende la compilazione di progetti di grandi dimensioni molto più veloce, perché il compilatore di solito trascorre la maggior parte del tempo l'analisi di file di intestazione, non il codice sorgente.Questo trucco spesso velocizzare la compilation di un fattore di due o più.

È questa la pena di fare?Sembra avere senso, ma è una cosa in più per tenere traccia di - ritengo che sarebbe molto più semplice, basta inserire il tutto nel file di intestazione.

È stato utile?

Soluzione

Assolutamente. Il C / C ++ costruire modello è ... ehm ... un anacronismo (a dire il migliore). Per i grandi progetti diventa una seria valle di lacrime.

Come Neil osserva correttamente, questo dovrebbe non essere l'approccio predefinito per il design di classe, non andare fuori strada a meno che non si ha realmente bisogno.

Rottura circolare includono riferimenti è l'unica ragione in cui si deve utilizzare in avanti dichiarazioni.

// a.h
#include "b.h"
struct A { B * a;  }

// b.h
#include "a.h"  // circlular include reference 
struct B { A * a;  }

// Solution: break circular reference by forward delcaration of B or A

Riduzione ricostruire tempo - Immaginate il seguente codice

// foo.h
#include <qslider>
class Foo
{
   QSlider * someSlider;
}

Ora ogni file cpp che tira direttamente o indirettamente, foo.h tira anche in QSlider.h e tutte le sue dipendenze. Che può essere centinaia di file cpp! (Intestazioni precompilate aiutare un po '- e, a volte molto - ma girano su disco / pressione CPU in pressione della memoria / disco, e quindi sono ben presto colpendo il limite "next")

Se l'intestazione richiede solo una dichiarazione di riferimento, questa dipendenza può essere spesso limitato ad alcuni file, per esempio foo.cpp.

La riduzione del tempo di costruzione incrementale - L'effetto è ancora più pronunciato, quando si tratta con il proprio (anziché biblioteca stabili) intestazioni. Immaginate di avere

// bar.h
#include "foo.h"
class Bar 
{
   Foo * kungFoo;
   // ...
}

Ora, se la maggior parte delle necessità del cpp per tirare in bar.h, ma anche indirettamente tirare in foo.h. Così, ogni cambio di foo.h innesca build di tutti questi file cpp (che potrebbe anche non aver bisogno di sapere Foo!). Se bar.h utilizza una dichiarazione anticipata per i Foo invece, la dipendenza da foo.h è limitato a bar.cpp:

// bar.h
class Foo;
class Bar 
{
   Foo * kungFoo;
   // ...
}

// bar.cpp
#include "bar.h"
#include "foo.h"
// ...

E 'così comune che si tratta di un modello - modello Pimpl . Il suo utilizzo è duplice: in primo luogo fornisce vero isolamento di interfaccia / implementazione, l'altro sta riducendo costruire dipendenze. In pratica, mi piacerebbe di peso la loro utilità 50:50.

È necessario un riferimento nell'intestazione, non si può avere un'esemplificazione diretta di tipo dipendente. Questo limita i casi in cui possono essere applicate le dichiarazioni previsionali. Se lo si fa esplicitamente, è comune l'uso di una classe di utilità (come boost :: scoped_ptr ) per questo.

è costruire tempo vale la pena? Sicuramente , direi. Nel momento peggiore caso di compilazione cresce polinomiale con il numero di file nel progetto. altre tecniche - come le macchine più veloci e parallele costruisce -. in grado di fornire solo i guadagni percentuali

Il più veloce la costruzione, il più delle volte gli sviluppatori di testare quello che hanno fatto, il più delle volte unit test eseguiti, costituiscono la più veloce pause possono essere trovati fisso, e meno spesso gli sviluppatori finiscono per procrastinare.

In pratica, la gestione del tempo di costruzione, mentre essenziale su un progetto di grandi dimensioni (ad esempio, centinaia di file di origine), si fa ancora una "differenza comfort" a piccoli progetti. Inoltre, l'aggiunta di miglioramenti dopo il fatto è spesso un esercizio di pazienza, come una singola soluzione potrebbe radersi solo pochi secondi (o meno) di una build di 40 minuti.

Altri suggerimenti

Io lo uso per tutto il tempo. La mia regola è che se non ha bisogno l'intestazione, poi ho messo una dichiarazione anticipata ( "utilizzare le intestazioni, se è necessario, utilizzare in avanti dichiarazioni se potete" ). L'unica cosa che fa schifo è che ho bisogno di sapere come la classe è stata dichiarata (struct / classe, forse se si tratta di un modello che ho bisogno dei suoi parametri, ...). Ma nella stragrande maggioranza delle volte, si tratta solo verso il basso per "class Slider;" o qualcosa che. Se qualcosa richiede un po 'più fastidio per essere appena dichiarato, si può sempre dichiarare una speciale avanti intestazione dichiarare come la standard fa con iosfwd troppo.

Non compreso il file di intestazione, non solo di ridurre il tempo di compilazione, ma sarà anche evitare di inquinare il namespace. I file, tra cui l'intestazione vi ringrazio per l'inclusione il meno possibile in modo che possano continuare a utilizzare un ambiente pulito.

Questo è il piano di massima:

/* --- --- --- Y.hpp */
class X;
class Y {
    X *x;
};

/* --- --- --- Y.cpp */
#include <x.hpp>
#include <y.hpp>

...

Ci sono puntatori intelligenti che sono specificamente progettati per funzionare con puntatori a tipi incompleti. Un uno molto noto è boost::shared_ptr .

Sì, certo aiuta. Un'altra cosa da aggiungere al vostro repertorio è intestazioni precompilate se siete preoccupati per il tempo di compilazione.

FAQ 39.12 e 39.13

La libreria standard fa questo per alcune delle classi iostream nell'intestazione <iosfwd> standard. Tuttavia, non è una tecnica generalmente applicabile -. Notare non ci sono tali intestazioni per gli altri tipi di libreria standard, e non dovrebbe (secondo me) è il vostro approccio alla progettazione di default gerarchie di classe

Anche se questo EEMS per essere un "ottimizzazione" preferito per i programmatori, ho il sospetto che come la maggior parte ottimizzazioni, alcuni di loro hanno effettivamente cronometrato l'accumulo dei loro progetti con e senza tali dichiarazioni. I miei esperimenti limitati in questo settore indicano che l'uso di intestazioni precompilate in compilatori moderni rende unecessary.

C'è una differenza enorme in tempi di compilazione per i grandi progetti, anche quelli con dipendenze gestita con attenzione. È meglio avere l'abitudine di dichiarare in avanti e mantenere il più possibile dai file di intestazione, perché a un sacco di negozi di software che utilizza C ++ è richiesto. La ragione per cui non si vede più di tanto nei file di intestazione standard è perché quelli fanno un largo uso di modelli, a quel punto in avanti dichiarando diventa difficile. Per MSVC è possibile utilizzare / P di dare uno sguardo a come il file pre-elaborato appare prima della compilazione vera e propria. Se non hai fatto alcuna dichiarazione in avanti nel progetto probabilmente sarebbe un'esperienza interessante per vedere quanto di elaborazione in più deve essere fatto.

In generale, no.

Ho usato per trasmettere dichiarare quanto ho potuto, ma non più.

Per quanto riguarda la Qt è interessato, si può notare che v'è un <QtGui> file include che tirerà in tutti i Widget GUI. Inoltre, ci C'è un <QtCore>, <QtWebKit>, <QtNetwork> ecc è un file di intestazione per ogni modulo. Sembra che il team di Qt crede che questo è il metodo preferito anche. Si dice così nella loro documentazione del modulo.

È vero, il tempo di compilazione può essere aumentata. Ma nella mia esperienza il suo solo non più di tanto. E se lo fosse, usando le intestazioni precompilate sarebbe il passo successivo.

Quando si scrive ...

includere "foo.h"

... si in tal modo incaricare un sistema di generazione convenzionale "Ogni volta che c'è alcun cambiamento whatsover nel file di libreria foo.h, scartare questa unità di compilazione e ricostruire, anche se tutto ciò che è accaduto a foo.h stata l'aggiunta di un commento, o l'aggiunta di un commento ad alcuni file di cui foo.h comprende, anche se tutto ciò che è successo è qualche collega ultra-esigenti riequilibrato le parentesi graffe, anche se non è successo niente di diverso da un collega di pressione registrato foo.h invariato e inavvertitamente ha cambiato il suo timestamp ".

Perché si vuole emettere un tale ordine? intestazioni Biblioteca, perché in generale hanno i lettori più umano di intestazioni di applicazione, hanno una particolare vulnerabilità ai cambiamenti che non hanno alcun impatto sul binario, come il miglioramento della documentazione delle funzioni e degli argomenti o l'urto di un numero di versione o copyright della data.

Le regole C ++ consentono namespace per essere riaperta in qualsiasi punto in un'unità di compilazione (a differenza di un struct o class ), al fine di per sostenere dichiarazione anticipata.

In avanti le dichiarazioni sono molto utili per rompere le dipendenze circolari, e, talvolta, può essere ok per l'uso con il proprio codice, ma il loro utilizzo con il codice della libreria può interrompere il programma su un'altra piattaforma o con altre versioni della libreria (questo accade anche con il tuo codice se non sei abbastanza attento).IMHO non ne vale la pena.

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