2015-01-13 8 views
5

Questa è una domanda specifica per C++ 11. Suppongo di avere già utilizzato un vettore std::vector<T> v e di ridimensionarlo a n elementi inizializzati con un valore esistente T val. (Usecase tipico: il vettore è il membro di un'istanza che viene riciclata).È std :: vector <T> :: ridimensionamento (n, val) sufficiente per l'inizializzazione?

Quali sono i pro/contro dei seguenti modi e quali sono i più efficienti?

1) È std::vector<T>::resize(n, val) sufficiente per l'inizializzazione?

v.clear(); 
v.resize(n, val); 

2) In caso contrario, presumo che quanto segue sia corretto?

v.clear(); 
v.resize(n); 
std::fill(v.begin(), v.end(), val); 

3) Che ne dici di scambiare?

v.swap(std::vector<T>(n, val)); 
+1

Se si tratta di un duplicato, si prega di collegamento alla soluzione, voterò per la chiusura. – Sheljohn

+0

Perché vuoi ridimensionarlo? Per liberare la memoria? – edmz

+0

@black Caso tipico: si tratta di un membro della classe e sto riciclando l'istanza. – Sheljohn

risposta

5

(4)

std::fill(v.begin(), std::min(v.begin() + n, v.end()), val); 
v.resize(n, val); 

Se T ha un comportamento assegnazione adatto che è almeno più economico di costruire uno nuovo, quindi utilizzare (4). Questo è il caso per T = int (assegnare e costruire sono gli stessi) e T = std :: stringa (l'assegnazione può essere più veloce della costruzione, in quanto potrebbe essere in grado di utilizzare il buffer esistente).

Se T ha lo stesso costo di assegnazione e costruzione (ad es. T = int), quindi (1) potrebbe anche essere utilizzato per chiarezza senza perdita di prestazioni.

Se T non può essere assegnato o, per qualche ragione, assegnazione è più costoso che costruire (raro) quindi usare (1)

(1) potrebbe essere semplificato utilizzando v.assign(n, val); (kudos a @Casey)

Non so se (4) sarebbe la stessa prestazione utilizzando assign. Non so se assegnare (ironico, dato il nome) assegnare i nuovi elementi a quelli esistenti, o ricostruirli di nuovo.

Modifica: un possibile miglioramento a (4), che non ho testato. Può evitare il sovraccarico di copia/spostamento durante una modifica della capacità vettoriale.

if (n <= v.capacity()) 
{ 
    std::fill(v.begin(), std::min(v.begin() + n, v.end()), val); 
    v.resize(n, val); 
} 
else 
{ 
    v.assign(n, val); 
} 
+0

Immagino che 4 sia vantaggioso solo se n è maggiore della dimensione attuale? – Sheljohn

+0

@ Sh3ljohn Ben individuato, ho effettuato un aggiornamento a (4) –

+0

Si può riempire scrivere oltre a v.end() se n> vecchia dimensione ?? –

3

(1) è sufficiente. (3) funziona anche. La differenza è che (1) non libera la memoria se la nuova dimensione è inferiore alla corrente. (3) assegna sempre un nuovo blocco di memoria ed elimina quello vecchio.

(2) è più lento di (1) in generale, poiché per prima cosa costruisce gli elementi, quindi li assegna. Potrebbe anche non essere compilato se T non è costruibile in modo predefinito.

+0

Buono :) Suppongo che la pratica e il test _ad hoc_ determineranno tra (1) e (3) a seconda dei casi. – Sheljohn

+1

Direi che 3 è la peggiore performance, se T è semplice. –

3

Consente di scomporlo per mostrare le differenze tra ognuna di queste. Userò n come nuova dimensione, m come la vecchia dimensione.

1.

v.clear();//keeps the same buffer, but calls the destructor on all the values 
v.resize(n, val);//only makes a new buffer if the value is bigger, does no moves. 

2.

v.clear();//keeps the same buffer, but calls the destructor on all the values 
v.resize(n);//initializes all the values to default 
std::fill(v.begin(), v.end(), val);//initializes all the values again to the new value 

3.

v.swap(std::vector<T>(n, val));//calls destructor on all values in v, cannot reuse the buffer, initializes all the values to the new value 

Le differenze sono sottili ma reale. 1 può (ma non è garantito) riutilizzare il buffer, che può risparmiare sull'overhead di memoria. 2 è uguale a 1 ma esegue una reinizializzazione doppia. 3 è uguale a 1 ma non può riutilizzare il buffer.

Personalmente penso che le differenze tra i tre siano troppo sottili per la maggior parte dei casi, e 1 è il più leggibile.

+0

Molto buono, e grazie per l'opinione personale su 1 vs 3 :) – Sheljohn

+0

1 È garantito il riutilizzo del buffer. –

+0

@NeilKirk iff n <= m, che non è specificato nel codice. Se questo è il caso, allora non dovrebbe ridimensionare, dato che l'incarico può essere più rapido della distruzione e della reinizializzazione. – IdeaHat

8

Perché non utilizzare l'interfaccia progettata esattamente per questo lavoro?

v.assign(n, val); 

Documentation Here

+0

Sapete se questa funzione è garantita per utilizzare l'operatore di assegnazione su qualsiasi elemento già esistente nel vettore? –

+0

L'implementazione dipende dall'implementatore della libreria. Lo standard non specifica come deve funzionare. Mi aspetterei che utilizzi l'assegnazione su articoli esistenti, la costruzione di copie sul posto su quelli nuovi e se il vettore si restringe di conseguenza, la distruzione in situ. –

+0

Se non è garantito, potrebbe non essere il modo più performante. –