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

Contenuto cancellato Contenuto aggiunto
Nessun oggetto della modifica
Nessun oggetto della modifica
Riga 9:
È 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, chiamando la ''placement -new'' sullo spazio ottenuto, ma non 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.
 
È piuttosto pericolosa, in quanto, se chiamata troppe volte o con un valore troppo grande, esaurisce lo stack, e, se chiamata per allocare lo spazio in cui verranno creati oggetti dotati di distruttore, provoca resourseresource leak.
 
=== 5.1.2. Sposta prima dei colli di bottiglia le allocazioni di memoria e dopo i colli di bottiglia le deallocazioni. ===
 
La gestione di memoria dinamica di dimensione variabile è molto più lenta della gestione della memoria sullo stack.

Analoga Analogo ragionamentoottimizzazione va fattofatta per le operazioni che indirettamente provocano allocazioni indirettamente, come la copia di oggetti che, direttamente o indirettamente, possiedono memoria dinamica.
 
=== 5.1.3. Prima di aggiungere elementi a oggetti “vector” o “string”, chiama “reserve” con una dimensione sufficiente per la maggior parte dei casi. ===
 
Se si aggiungono ripetutamente elementi a oggetti “vector” o “string”, ogni tanto viene eseguita una costosa operazione di riallocazione del contenuto. Per evitarlaevitare tali costose riallocazioni, basta allocare inizialmente lo spazio che probabilmente sarà necessariosufficiente.
 
=== 5.1.4. Per svuotare un oggetto " ''vector<T> x"'' senza deallocarne la memoria, usa l’istruzione "''x.resize(0);"''; per svuotarlo deallocandone la memoria, usa l’istruzione " ''vector<T>().swap(x);"''. ===
 
Per svuotare un oggetto ''vector'', esiste anche l’istruzione “clear''clear()''; tuttavia lo standard non specifica se tale istruzione conserva la capacità (capacity) del ''vector'' oppure no. Se questa istruzione deve essere eseguita spesso, e si vuole essere sicuri di evitare frequenti riallocazioni, si deve chiamare la “resize”funzione ''resize'', che, secondo lo standard, conserva sicuramente la capacità. Se invece si vuole essere sicuri di liberare la memoria usata da tale collezione, in quanto per un po’ di tempo tale oggetto non verrà più usato usato, oppure verrà usato con un numero molto più piccolo di elementi, si deve chiamare la “swap” su un nuovo oggetto temporaneo vuoto.
 
=== 5.1.5. Per ogni classe concreta T che, direttamente o indirettamente, possiede della memoria dinamica, definisciridefinisci le appropriate funzioni “swap”''swap''. ===
 
In particolare, aggiungi alla classe una funzione membro ''public'' con la seguente firma:
void swap(T&) throw();
e nello stesso namespace che contiene la classe T aggiungi la seguente funzione non-membro:
void swap(T& lhs, T& rhs) { lhs.swap(rhs); }
e, se la classe non è un template di classe, nello stesso file in cui è dichiarata la classe T aggiungi la seguente funzione non-membro:
namespace std { template<> swap(T& lhs, T& rhs) { lhs.swap(rhs); } } ===
 
Nella libreria standard la funzione std::swap viene richiamata in molti algoritmi. Tale funzione ha una implementazione generica e specializzazioni per vari tipi della libreria standard.
Line 40 ⟶ 42:
L'implementazione generica di swap comporta la creazione e 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 ''swap''.

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 devedevono fareridefinire niente.funzioni ''swap''.

Se la classe è astratta, la funzione ''swap'' non dovrebbe mai essere chiamata la funzione swap sugli oggetti di tale tipo, e quindi anche in questo caso non si devedevono fareridefinire nientefunzioni ''swap''.
 
Per velocizzare la funzione swap, la si deve specializzare per la propria classe. Ci sono due modi possibili: nel namespace della stessa classe (che può essere quello globale) come overload, o nel namespace std 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 48 ⟶ 54:
Tale lavoro deve consistere nell'effettuare lo swap di tutti i membri non-static dei due oggetti, tipicamente chiamando la funzione swap su di essi, senza qualificarne il namespace.
 
Per consentire di trovare la funzione ''std:: swap'', la funzione deve iniziare con la seguente istruzione:
 
using std::swap;