Ottimizzare C++/Scrivere codice C++ efficiente/Costruzioni e distruzioni: differenze tra le versioni

Contenuto cancellato Contenuto aggiunto
RamaccoloBot (discussione | contributi)
m Bot: Sostituzione automatica (-[[Categoria:Ottimizzare C++|Ottimizzare C++/Scrivere codice C++ efficiente/ +[[Categoria:Ottimizzare C++|)
Nessun oggetto della modifica
Riga 1:
{{Ottimizzare C++}}
 
La costruzione e la distruzione di un oggetto richiederichiedono tempo.
 
In questa sezione vengono proposte linee-guida per ridurre il numero di creazione di oggetti, e quindi della loro corrispondente distruzione.
Riga 9:
'''Dichiara le variabili il più tardi possibile.'''
 
Dichiarare una variabile il più tardi possibile, significa sia dichiararla nell'ambito (in inglese, ''scope'') più stretto possibile, sia dichiararla il più avanti possibile entro quell'ambito.
Essere nell'ambito più stretto possibile comporta che se tale ambito non viene mai eseguito, l'oggetto associato alla variabile non viene mai costruito né distrutto.
Essere il più avanti possibile all’internoall'interno di un ambito comporta che se prima di tale dichiarazione c'è un'uscita prematura, tramite <code>return</code> o <code>break</code> o <code>continue</code>, l'oggetto associato alla variabile non viene mai costruito né distrutto.
 
Inoltre, spesso all'inizio di una routine non si ha un valore appropriato per inizializzare l'oggetto associato alla variabile, e quindi si è costretti a inizializzarla con un valore di default, e poi assegnarle il valore appropriato.
Se invece la si dichiara quando si ha a disposizione il valore appropriato, la si può inizializzare con tale valore senza bisogno di fare un successivo assegnamento, come suggerito dalla linea-guida "Inizializzazioni" in questa sezione.
 
=== Inizializzazioni ===
Riga 20:
'''Usa inizializzazioni invece di assegnamenti. In particolare, nei costruttori usa le liste di inizializzazione.'''
 
SeAnche se un oggetto'istanza di una classe non viene inizializzatoinizializzata esplicitamente, viene comunque inizializzatoinizializzata automaticamente dal costruttore di default.
In generale, chiamare il costruttore di default seguito da un assegnamento di un valore è meno efficiente o ugualmente efficiente che chiamare solo un costruttore con tale valore.
Quindi, invece di scrivere:
 
<source lang=cpp>
string s;
...
s = "abc"
</source>
 
scrivi:
 
<source lang=cpp>
string s("abc");
</source>
 
=== Operatori di incremento/decremento ===
Line 29 ⟶ 42:
Se l'oggetto incrementato è di un tipo fondamentale, non ci sono differenze tra le due forme, ma se si tratta di un tipo composito, l'operatore postfisso comporta la creazione di un inutile oggetto temporaneo, mentre l'operatore prefisso no.
 
Siccome ogni oggetto che è attualmente di un tipo fondamentale potrebbe diventare in futuro di una classe, è bene usare sempre quelll'operatore che in quest'ultimo caso è più efficiente.
 
SeTuttavia, invecese il valore dell'espressione formata dall’operatoredall'operatore di incremento o decremento viene usata in un'espressione più grande, potrebbe essere opportuno usare l'operatore postfisso.
 
=== Operatori compositi di assegnamento ===
Line 37 ⟶ 50:
'''Usa gli operatori compositi di assegnamento (come in <code>a += b</code>) invece degli operatori semplici combinati con operatori di assegnamento (come in <code>a = a + b</code>).'''
 
Tipicamente un operatore semplice, come nell'espressione ''<code>a + b''</code>, crea un oggetto temporaneo.
Per esempio, nel seguente codice, gli operatori ''<code>+''</code> creano stringhe temporanee, la cui creazione e distruzione richiede tempo:
 
<source lang=cpp>
Line 45 ⟶ 58:
</source>
 
Il seguente codice, equivalente al precedente, risulta più efficiente, in quanto l'operatore ''<code>+=''</code> non crea oggetti temporanei:
 
<source lang=cpp>
Line 56 ⟶ 69:
=== Passaggio di argomenti alle funzioni ===
 
'''Quando devi passare un argomento <code>x</code> di tipo <code>T</code> a una funzione, usa il seguente criterio:'''
* '''Se <code>x</code> è un argomento di solo input,'''
** '''se <code>x</code> può essere nullo,'''
*** '''passalo per puntatore a costante (<code>const T* x</code>),'''
** '''altrimenti, se <code>T</code> è un tipo fondamentale o un iteratore o un oggetto-funzione,'''
*** '''passalo per valore (<code>T x</code>) o per valore costante (<code>const T x</code>),'''
** '''altrimenti,'''
*** '''passalo per riferimento a costante (<code>const T& x</code>),'''
* '''altrimenti, cioè se <code>x</code> è un argomento di solo output o di input/output,'''
** '''se <code>x</code> può essere nullo,'''
*** '''passalo per puntatore a non-costante (<code>T* x</code>),'''
** '''altrimenti,'''
*** '''passalo per riferimento a non-costante (<code>T& x</code>).'''
 
Il passaggio per riferimento è più efficiente del passaggio per puntatore in quanto facilita al compilatore l’eliminazionel'eliminazione della variabile, e in quanto il chiamato non deve verificare se il riferimento è valido o nullo; tuttavia, il puntatore ha il pregio di poter rappresentare un valore nullo, ed è più efficiente passare solo un puntatore, che un riferimento a un oggetto insieme a un booleano che indica se tale riferimento è valido.
 
Per oggetti che possono essere contenuti in uno o due registri, il passaggio per valore è più efficiente o ugualmente efficiente del passaggio per riferimento, in quanto tali oggetti possono essere contenuti in registri e non hanno livelli di indirettezza, pertanto questo è il modo più efficiente di passare oggetti sicuramente piccoli, come i tipi fondamentali, gli iteratori e gli oggetti-funzione.
Per oggetti più grandi di due registri, il passaggio per riferimento è più efficiente del passaggio per valore, in quanto taliil oggettipassaggio nonper devonovalore comporta la copia di tali oggetti esserenello copiatistack.
 
Un oggetto composito veloce da copiare potrebbe essere efficientemente passato per valore, ma, a meno che si tratti di un iteratore o di un oggetto-funzione, per i quali si assume l’efficienza della copia, tale tecnica è rischiosa, in quanto l’oggetto potrebbe diventare in futuro più lento da copiare.
Per esempio, se un oggetto di classe <code>Point</code> contiene solo due <code>float</code>, potrebbe essere efficientemente passato per valore; ma se in futuro si aggiungesse un terzo <code>float</code>, o se i due <code>float</code> diventassero due <code>double</code>, potrebbe diventare più efficiente il passaggio per riferimento.
 
Infine, è bene usare lo specificatore <code>const</code> per tutti i dati che non verranno modificati, in quanto tale informazione consente al compilatore alcuni tipi di ottimizzazione.
 
=== Dichiarazione <code>explicit</code> ===
Line 82 ⟶ 97:
'''Dichiara <code>explicit</code> tutti i costruttori che possono ricevere un solo argomento, eccetto i costruttori di copia delle classi concrete.'''
 
I costruttori implicitinon-<code>explicit</code> possono essere chiamati automaticamente dal compilatore che esegue una conversione automatica.
L'esecuzione Adi seconda della complessità deltale costruttore, tale chiamata può richiedere molto più tempo del necessario.
Rendendo obbligatoriamente esplicita tale conversione, il compilatore potrebbe scegliere un'altra funzione in overload, evitando così di chiamare il costruttore, oppure segnalare l'errore e costringere il programmatore a scegliere un’altraun'altra strada per evitare la chiamata al costruttore.
 
Per i costruttori di copia delle classi concrete si deve fare eccezione, per consentirne il passaggio per valore.
Per le classi astratte, anche i costruttori di copia possono essere dichiarati <code>explicit</code>, in quanto, per definizione, le classi astratte non si possono istanziare, e quindi gli oggetti di tale tipo non dovrebbero mai essere passati per valore.
 
=== Operatori di conversione ===
 
'''Non dichiarare operatori di conversione, se non per mantenere la compatibilità con una libreria obsoleta (in C++0x, dichiarali <code>explicit</code>).'''
 
Gli operatori di conversioniconversione consentono conversioni implicite, e quindi incorrono nello stesso problema dei costruttori impliciti, descritto nella linea-guida "Dichiarazione <code>explicit</code>" di questa sezione.
 
Se tali conversioni sono necessarie, fornisci invece una funzione membro equivalente, che può essere chiamata solo esplicitamente.
 
L'unico utilizzo che rimane accettabile per gli operatori di conversione si ha quando si stavuole convertendofar convivere una grande mole di software dall'uso di unanuova libreria all'uso dicon un'altra vecchia libreria simile.
In Durantetal la fase di transizione, in cui le due librerie coesistonocaso, può essere comodo avere operatori che convertono automaticamente gli oggetti dai tipi della vecchia libreria ai tipi della nuova libreria e viceversa.
 
=== Idioma ''Pimpl'' ===
 
'''Non usare sistematicamenteUsa l'idioma [[w:en:Opaque pointer|''Pimpl'', ma]] solosolamente quando vuoi rendere il resto del programma indipendente dall'implementazione di una classe.'''
 
L'idioma ''Pimpl'' (che significa Puntatore a IMPLementazione) consiste nel memorizzare nell'oggetto solamente un puntatore alla struttura che contiene tutte le informazioni utili di tale oggetto.
Line 115 ⟶ 134:
 
[[Categoria:Ottimizzare C++|Costruzioni e distruzioni]]
{{Avanzamento|75100%|2325 maggio 2008}}