Ottimizzare C++/Ottimizzazione del codice C++/Allocazione e deallocazione: differenze tra le versioni

Contenuto cancellato Contenuto aggiunto
RamaccoloBot (discussione | contributi)
Riportate modifiche da en.wikibooks
Riga 2:
 
Per quanto sia efficiente l'allocatore, le operazioni di allocazione e deallocazione, richiedono parecchio tempo, e spesso l'allocatore non è molto efficiente.
 
Qui si descrivono tecniche per ridurre il numero complessivo di allocazioni di memoria, e quindi delle corrispondenti deallocazioni, che si possono adottare nei colli di bottiglia, cioè dopo aver constatato che il grande numero di allocazioni ha un impatto significativo sulle prestazioni.
In questa sezione si descrivono alcune tecniche per ridurre il numero complessivo di allocazioni di memoria, e quindi delle corrispondenti deallocazioni.
QuiSono sida descrivonoadottare tecniche per ridurre il numero complessivo di allocazioni di memoria, e quindi delle corrispondenti deallocazioni, che si possono adottaresolamente nei colli di bottiglia, cioè dopo aver constatato che il grande numero di allocazioni ha un impatto significativo sulle prestazioni.
 
=== La funzione <code>alloca</code> ===
Line 12 ⟶ 14:
È una funzione non-standard, ma presente in molti compilatori per vari sistemi operativi.
 
Può essere usata anche per allocare un array di oggetti che hanno un costruttore, chiamandopurché lasi chiami l''placement-operatore <code>new''</code> di piazzamento sullo spazio ottenuto, ma non dovrebbe essere usata per array di oggetti che hanno un distruttore o che, direttamente o indirettamente, contengono membri che hanno un distruttore, in quanto tali distruttori non verrebbero mai chiamati.
 
Tuttavia, è piuttosto pericolosa, in quanto, se chiamata troppe volte o con un valore troppo grande, esaurisce lo stack, e, se chiamatausata per allocare lo spazio in cui verranno creati oggetti dotatiaventi diun distruttore, provoca resource leak.
Pertanto non si consiglia di fareusare uncon usogrande indiscriminato dimoderazione questa funzione.
 
=== Spostare le allocazioni e le deallocazioni ===
Line 27 ⟶ 29:
=== La funzione <code>reserve</code> ===
 
'''Prima di aggiungere elementi a oggettiun oggetto <code>vector</code> o <code>string</code>, chiama la sua funzione membro <code>reserve</code> con una dimensione sufficiente per la maggior parte dei casi.'''
 
Se si aggiungono ripetutamente elementi a oggetti <code>vector</code> o <code>string</code>, ogni tanto viene eseguita una costosa operazione di riallocazione del contenuto.
Line 34 ⟶ 36:
=== Mantenere la capacità dei <code>vector</code> ===
 
'''Per svuotare un oggetto <code>x</code> di tipo <code>vector<T> x</code> senza deallocarne la memoria, usa l'istruzione <code>x.resize(0);</code>; per svuotarlo deallocandone la memoria, usa l'istruzione <code>vector<T>().swap(x);</code>.'''
 
Per svuotare un oggetto <code>vector</code>, esiste anche l'istruzionela funzione membro <code>clear()</code>; tuttavia lo standard C++ non specifica se tale istruzione conserva la capacità allocata del <code>vector</code> oppure no.
 
Se riempi e svuoti ripetutamente un oggetto <code>vector</code>, e quindi vuoi essere sicuro di evitare frequenti riallocazioni, esegui lo svuotamento chiamando la funzione <code>resize</code>, che, secondo lo standard, conserva sicuramente la capacità per il successivo riempimento.
 
Se invece hai finito di usare un oggetto <code>vector</code> di grandi dimensioni, e per un po' di tempo non lo userai più, oppure lo userai con un numero molto più piccolo di elementi, e quindi vuoi essere sicuro di liberare la memoria usata da tale collezione, chiama la funzione <code>swap</code> su un nuovo oggetto temporaneo <code>vector</code> vuoto.
 
=== Ridefinire la funzione <code>swap</code> ===
 
'''Per ogni classe concreta copiabile <code>T</code> che, direttamente o indirettamente, possiede della memoria dinamica, ridefinisci le appropriate funzioni <code>swap</code>.'''
 
In particolare, aggiungi alla classe una funzione membro <code>public</code> con la seguente firma:
 
<source lang=cpp>
void swap(T&) throw();
</source>
 
e aggiungi la seguente funzione non-membro nello stesso namespace che contiene la classe <code>T</code>:
 
<source lang=cpp>
void swap(T& lhs, T& rhs) { lhs.swap(rhs); }
</source>
 
e, se la classe non è un template di classe, aggiungi la seguente funzione non-membro nello stesso file inche cuicontiene èla dichiaratadefinizione ladella classe <code>T</code>:
 
<source lang=cpp>
namespace std { template<> swap(T& lhs, T& rhs) { lhs.swap(rhs); } }
</source>
 
Nella libreria standard la funzione <code>std::swap</code> viene richiamata frequentemente da molti algoritmi.
Tale funzione ha una implementazione generica e ha implementazioni specializzate per vari tipi della libreria standard.
 
Se gli oggetti di una classe non-standard vengono usati in algoritmi della libreria standard, e non viene fornito un overload della funzione <code>swap</code>, viene usata l'implementazione generica.
 
L'implementazione generica di <code>swap</code> comporta la creazione e la distruzione di un oggetto temporaneo e l'esecuzione di due assegnamenti di oggetti.
Tali operazioni richiedono molto tempo se applicate ad oggetti che possiedono della memoria dinamica, in quanto tale memoria viene riallocata per tre volte.
 
Il possesso di memoria dinamica citato nella linea-guida può essere anche solo indiretto. Per esempio, se una variabile membro è un oggetto string o vector, o è un oggetto che contiene un oggetto string o vector, la memoria posseduta da tali oggetti viene riallocata ogni volta che si copia l'oggetto che li contiene. Quindi anche in tali casi si devono ridefinire le funzioni <code>swap</code>.
Line 69 ⟶ 78:
Se l'oggetto non possiede memoria dinamica, la copia dell'oggetto è molto più veloce, e comunque non sensibilmente più lenta che usando altre tecniche, e quindi non si devono ridefinire funzioni <code>swap</code>.
 
Se la classe non è copiabile o è astratta, la funzione <code>swap</code> non dovrebbe mai essere chiamata sugli oggetti di tale tipo, e quindi anche in questo caso non si devono ridefinire funzioni <code>swap</code>.
 
Per velocizzare la funzione <code>swap</code>, la si deve specializzare per la propria classe.
Ci sono due modi possibili per farlo: nel namespace della stessa classe (che può essere quello globale) come overload, oppure nel namespace <code>std</code> come specializzazione del template standard.
È meglio definirla in entrambi i modi, in quanto, in primo luogo, se si tratta di un template di classe solo il primo modo è possibile, e poi alcuni compilatori non accettano o segnalano un avvertimento se è definita solo nel primo modo.
 
Line 79 ⟶ 88:
Tale lavoro deve consistere nello scambiare tutti i membri non-static dei due oggetti, tipicamente chiamando la funzione <code>swap</code> su di essi, senza qualificarne il namespace.
 
Per consentire di trovare la funzione <code>std::swap</code>, la funzione membro deve iniziare con la seguente istruzione:
 
<source lang=cpp>
using std::swap;
</source>
 
[[Categoria:Ottimizzare C++|Ottimizzazione del codice C++/Allocazione e deallocazione]]