2011-10-06 11 views
8
#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(const 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(std::move(A())); 
} 

int main() 
{ 
    vec v; 

    foo(v); 
    foo(v); 
} 

L'esempio precedente produce output successivo:Spostamento di oggetti temporanei in un vettore

constructor 1 
move 1 
destructor 1 
constructor 2 
move 2 
move 1 
destructor 1 
destructor 2 
destructor 1 
destructor 2 

Domande:

  1. Perché viene eseguito il primo distruttore (ma non viene eseguito per il 2 ° oggetto)?
  2. Perché lo spostamento del secondo oggetto, eseguito prima dello spostamento del primo oggetto?
  3. Perché alla fine vengono eseguiti due distruttori per ciascun oggetto?

PS Ho appena controllato, e gli oggetti sono infatti posizionato come previsto (il primo va alla posizione 0 nel vettore, e il 2 ° va alla posizione 1 del vettore)

PPS If it questioni, io sto usando GCC 4.3, e ho compilare il programma in questo modo:

g++ n1.cpp -Wall -Wextra -pedantic -ansi -std=c++0x -O3 
+5

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

risposta

8

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"; 
} 
  1. ho rimosso il const dal costruttore mossa.
  2. Ho rimosso il std::move dal push_back (è superfluo).
  3. 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 
  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.

  1. 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.

  1. 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.

4

il vettore sta aumentando la sua capacità e spostare gli elementi interni durante la chiamata a push_back.

5

uso giudizioso di reserve risolve metà del tuo problema: http://ideone.com/5Lya6 riducendo il numero di mosse inaspettate (che non si esplicitamente richiesta)

Inoltre, non dimenticare che il distruttore della temperatura sarà ancora il fuoco dopo dopo essere stato spostato nel vettore. Questo è il motivo per cui è necessario accertarsi che il temporizzatore rimanga nello stato corretto,, anche dopo l'assegnazione/costruzione del movimento.

#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(const 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(std::move(A())); 
} 

int main() 
{ 
    vec v; 
    v.reserve(2); 

    foo(v); 
    foo(v); 
} 
2

Un costruttore di movimento non "distrugge" l'oggetto spostato.

#include <iostream> 

struct Foo { 
    int i; 
    bool active; 

    Foo(int i): i(i), active(true) {} 
    Foo(Foo&& rhs): i(rhs.i), active(rhs.active) { rhs.active = false; } 
    Foo(Foo const& rhs): i(rhs.i), active(rhs.active) {} 
    ~Foo() { std::cout << i << (active ? " active": " inactive") << "\n"; } 
}; 


int main() { 
    Foo foo; 
    Bar bar(std::move(foo)); 
} 

Il output dà:

1 active 
1 inactive 
Problemi correlati