ho un po 'ricodificato il tuo esempio:
#include <iostream>
#include <utility>
#include <vector>
int i = 0;
struct A
{
A() : j(++i)
{
std::cout<<"constructor "<<j<<std::endl;
}
A(const A & c) : j(c.j)
{
std::cout<<"copy "<<j<<std::endl;
}
A(A && c) : j(c.j)
{
std::cout<<"move "<<j<<std::endl;
}
~A()
{
std::cout<<"destructor "<<j<<std::endl;
}
int j;
};
typedef std::vector<A> vec;
void foo(vec & v)
{
v.push_back(A());
}
int main()
{
vec v;
std::cout << "A\n";
foo(v);
std::cout << "B\n";
foo(v);
std::cout << "C\n";
}
- ho rimosso il
const
dal costruttore mossa.
- Ho rimosso il
std::move
dal push_back
(è superfluo).
- Ho inserito marcatori tra le chiamate a
foo
.
Per me Questo stampa simile al codice:
A
constructor 1
move 1
destructor 1
B
constructor 2
move 2
copy 1
destructor 1
destructor 2 // 1
C
destructor 2
destructor 1
- Perché la prima distruttore eseguito (ma non viene eseguito per la 2 ° oggetto)?
La 2a distruttore viene eseguita per il secondo oggetto alla linea contrassegnata // 1
. Questa è la distruzione del A()
temporaneo alla fine della seconda chiamata a push_back
.
- Perché è mossa del secondo oggetto, eseguito prima dello spostamento del 1 ° oggetto?
Nota: Per me il primo oggetto viene copiato, non è mosso, più che al di sotto.
Risposta: Eccezione sicurezza.
Spiegazione: Durante questo push_back
il vettore rileva che ha un buffer completo (di uno) e deve creare un nuovo buffer con spazio per due. Crea il nuovo buffer. Quindi sposta il secondo oggetto in quel buffer (alla fine di esso). Se questa costruzione genera un'eccezione, il buffer originale è ancora intatto e lo vector
rimane invariato. Altrimenti gli elementi vengono spostati o copiati dal primo buffer al secondo buffer (quindi spostare/copiare il primo elemento in secondo luogo).
Se A
ha un noexcept
, il costruttore di movimento a move
verrà utilizzato per spostarlo dal vecchio buffer al nuovo. Tuttavia, se il costruttore di movimento non è noexcept
, verrà utilizzato un copy
. Questo è di nuovo per la sicurezza delle eccezioni. Se il movimento dal vecchio buffer al nuovo può fallire, allora il vecchio buffer deve essere lasciato intatto in modo che lo vector
possa essere ripristinato allo stato originale.
Se aggiungo noexcept
al vostro movimento costruttore:
A(A && c) noexcept : j(c.j)
{
std::cout<<"move "<<j<<std::endl;
}
Poi la mia uscita è:
A
constructor 1
move 1
destructor 1
B
constructor 2
move 2
move 1
destructor 1 // 2
destructor 2
C
destructor 2
destructor 1
Nota la linea marcata // 2
è la distruzione del primo elemento dal vecchio tampone, dopo è stato spostato nel nuovo buffer.
- Perché sono alla fine due distruttori per ogni oggetto eseguiti?
Questo è il marchio distruzione del vector
e quindi la distruzione di ciascuno degli elementi s' il vector
.
Nota: 1) I costruttori di spostamento in cui il parametro è "const T &&" è quasi certamente privo di senso. Vuoi solo "T &&" così puoi, beh, spostare lo stato del lato destro. 2) Non è necessario spostare valori temporanei. – GManNickG