Ottimizzare C++/Scrivere codice C++ efficiente/Costrutti che peggiorano le prestazioni: differenze tra le versioni

Contenuto cancellato Contenuto aggiunto
RamaccoloBot (discussione | contributi)
Riportate modifiche da en.wikibooks
Riga 3:
Rispetto al linguaggio C, il linguaggio C++ aggiunge alcuni costrutti, il cui utilizzo peggiora l'efficienza.
 
Alcuni di tali costruttiessi sono piuttosto efficienti, ed èe quindi del tuttoli ragionevolesi pagarnepuò ilusare costotranquillamente quando servono, ma èsi altrettantodovrebbe ragionevoleevitare nondi pagarne il costo quando non li si usa.
 
Altri costrutti sono invece alquanto inefficienti, e devono quindi essere usati con grande parsimonia.
 
In questa sezione si presentano le linee-guida per evitare i costi dei costrutti C++ che peggiorano le prestazioni.
Riga 11:
=== L'operatore <code>throw</code> ===
 
'''Chiama l'operatore <code>throw</code> solamente quando si dovràvuoi avvisare l'un utente del fallimento del comando corrente.'''
 
Il sollevamento di una eccezione ha un costo molto elevato, dell'ordinerispetto deia 10000quello di una chiamata di funzione. Sono migliaia di cicli di processore.
Se tale operazione viene effettuata solamente ogni volta che un messaggio viene mostrato all'utente o scritto in un file di log, si ha la garanzia che non verrà eseguita troppo spesso senza che ce ne si accorga.
SeInvece, invece lase si effettuasollevano comeeccezioni operazioneper algoritmicascopi algoritmici, anche se pensatatali operazioni sono state pensate inizialmente per essere eseguitaeseguite raramente, potrebbepotrebbero finire per essere eseguitaeseguite troppo frequentemente.
 
=== Funzioni membro <code>static</code> ===
Riga 27:
=== Funzioni membro <code>virtual</code> ===
 
'''In ogni classe, definisci <code>virtual</code> il distruttore se e solo se la classe contiene almeno un'altra funzione membro <code>virtual</code>, e, a parte i costruttori e il distruttore, definisci <code>virtual</code> solamente le funzioni membro diche cuiritieni prevedipossa laessere necessitàutile di una ridefinizioneridefinire.'''
 
A parità di condizioni, le classiClassi che contengono almeno una funzione membro <code>virtual</code> occupano un po' più di spazio delle classi che non ne contengono, e. leLe istanze delle classi che contengono almeno una funzione membro <code>virtual</code> occupano un po' più di spazio (tipicamente, un puntatore ed eventualmente dello spazio di allineamento) e la loro costruzione richiede un po' più di tempo (tipicamente, per impostare tale puntatore) rispetto aglialle oggettiistanze di classi cheprive non contengonodi funzioni membro <code>virtual</code>.
 
Inoltre, leogni funzionifunzione membro <code>virtual</code> occupano un po'è più di spazio e sono più lentelenta da chiamare delledi un'identica funzionifunzione membro non-<code>virtual</code>.
 
=== Derivazione <code>virtual</code> ===
 
'''Usa la derivazione <code>virtual</code> solo quando due o più classi devono condividere la rappresentazione di una classe base comune.'''
 
Le funzioni membro delle classi base derivate in modo <code>virtual</code> sono un po' più lente delle funzioni membro derivate in modo non-<code>virtual</code>.
 
Per esempio, considera le seguenti definizioni di classe:
Line 47 ⟶ 45:
</source>
 
Con tali definizioni, ogni oggetto di classe C contiene due oggetti distinti di classe A, uno facenteereditato parte delladalla classe base B1, e l'altro facente parteereditato delladalla classe base B2.
 
Questo non costituisce un problema se la classe A non ha nessuna variabile membro non-<code>static</code>.
 
Se invece tale oggetto di classe A contiene qualche variabile membro, e si intende che tale variabile membro debba essere unica per ogni oggettoistanza di classe C, si deve usare la derivazione <code>virtual</code>, nel seguente modo:
 
<source lang=cpp>
Line 61 ⟶ 59:
 
Questa situazione è l'unica in cui è necessaria la derivazione <code>virtual</code>.
 
Se si usa la derivazione <code>virtual</code>, le funzioni membro della classe A sono un po' più lente da chiamare su un oggetto di classe C.
 
=== Template di classi polimorfiche ===
Riga 66:
'''Non definire template di classi polimorfiche.'''
 
In altre parole, non usare le parole-chiave "<code>template</code>" e "<code>virtual</code>" nella stessa definizione di classe.
I template di classe, ogni volta che vengono istanziati, producono una copia delle funzioni utilizzate della classe, e se tali classi contengono funzioni virtuali, vengono replicate anche le ''vtable'' e le informazioni ''RTTI''.
 
I template di classe, ogni volta che vengono istanziati, producono una copia delledi funzionitutte utilizzatele dellafunzioni classemembro utilizzate, e se tali classi contengono funzioni virtuali, vengono replicate anche le ''vtable'' e le informazioni ''RTTI''.
Questi dati ingrandiscono eccessivamente il programma.
 
=== Annullamento dell'argomento di <code>delete</code> ===
 
'''Non annullare un puntatore dopo aver chiamato <code>delete</code> su di esso, se sei sicuro che tale puntatore non verrà più usato.'''
 
L'uso più tipico dell'operatore <code>delete</code> si ha quando in un distruttore si dealloca un oggetto posseduto dall'oggetto in corso di distruzione; in tale contesto, si chiama <code>delete</code> su una variabile membro di tipo puntatore.
 
Dato che solitamente un distruttore non è così complicato da faticare a capire quali parti dell'oggetto corrente sono già state distrutte e quali non ancora, e dato che il puntatore usato per la chiamata a <code>delete</code> cesserà di esistere alla fine del distruttore stesso, anche se si lascia il valore corrente del puntatore non più valido, non si corre il rischio di usarlo accidentalmente.
 
D'altra parte, annullare il puntatore richiede una seppur piccolissima quantità di tempo.
 
La regola di annullare sempre il puntatore a cui è stato applicato l'operatore <code>delete</code> può essere adottata come tecnica di collaudo o di debugging, ma solamente nella generazione di una versione del software destinata ad attività di collaudo o di debugging.
 
=== Uso di deallocatori automatici ===
Line 87 ⟶ 77:
La garbage collection, cioè il recupero automatico della memoria non più referenziata, fornisce la comodità di non doversi occupare della deallocazione della memoria, e previene i [[w:Memory leak|''memory leak'']].
Tale funzionalità non viene fornita dalla libreria standard, ma viene fornita da librerie non-standard.
Tuttavia, tale tecnica di gestione della memoria offrepotrebbe produrre prestazioni peggiori dellarispetto alla deallocazione esplicita (cioè chiamando l'operatore <code>delete</code>).
 
La libreria standard del C++98 contiene un solo smart-pointer, l'<code>auto_ptr</code>, che è efficiente. Altri smart-pointer sono forniti da librerie non-standard, come Boost, o verranno forniti dal C++0x.
Line 93 ⟶ 83:
In particolare, compilando con l'opzione di gestione del multithreading, tali puntatori hanno pessime prestazioni nella creazione, distruzione e copia dei puntatori, in quanto devono garantire la mutua esclusione delle operazioni.
 
Normalmente bisognerebbe, in fase di progettazione, cercare di assegnare ogni oggetto allocato dinamicamente ad un proprietario, cioè a un altro oggetto che lo possiede, nel senso che avrà la responsabilità di distruggerlo.
Solo quando tale assegnazione risulta difficile, in quanto più oggetti tendono a rimpallarsi la responsabilità di distruggere l'oggetto, risulta opportuno usare uno smart-pointer con reference-count per gestire tale oggetto.
 
Line 100 ⟶ 90:
'''Definisci <code>volatile</code> solamente quelle variabili che vengono modificate in modo asincrono da dispositivi hardware o da più thread.'''
 
L'uso del modificatore <code>volatile</code> impedisce al compilatore di allocare una variabile in un registro, anche per un breve periodo.
Questo garantisce che tutti i dispositivi e tutti i thread ''vedano'' la stessa variabile, ma rende molto più lente le operazioni che manipolano tale variabile.