2011-10-06 16 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); 
} 

Powyższy przykład wywołuje następną wyjście:Moving tymczasowych obiektów do wektora

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

pytania:

  1. Dlaczego 1st destructor wykonany (ale to nie jest wykonywany na 2 obiekt)?
  2. Dlaczego ruch 2 obiektu jest wykonywany przed przesunięciem pierwszego obiektu?
  3. Dlaczego na końcu znajdują się dwa destruktory dla każdego obiektu?

PS Właśnie sprawdziłem, a obiekty są rzeczywiście umieszczone zgodnie z oczekiwaniami (1st przechodzi do pozycji 0 w wektorze i 2nd przechodzi do pozycji 1 w wektorze)

PPS czy to sprawy, Używam gcc 4.3 i skompilować program tak:

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

Uwaga: 1) Konstruktory ruchu, w których parametr jest "stały T &&" są prawie na pewno bezsensowne. Chcesz tylko "T &&", dzięki czemu możesz, dobrze, przesunąć stan prawej strony. 2) Nie musisz przenosić tymczasowych wartości. – GManNickG

Odpowiedz

8

mam lekko recoded swój przykład:

#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. Mam usunięte const od konstruktora przenieść.
  2. Usunąłem std::move z push_back (jest zbędne).
  3. Umieściłem znaczniki między połączeniami do foo.

Dla mnie to wypisuje podobny do kodu:

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. Dlaczego 1st destructor wykonany (ale to nie jest wykonywany dla 2nd obiektu)?

The 2nd destructor jest wykonywany na 2. przedmiotu na linii oznaczonej // 1. Jest to zniszczenie tymczasowego A() po zakończeniu drugiego połączenia pod numerem push_back.

  1. Dlaczego akcja 2nd obiektu, wykonywany przed przeniesieniem 1. obiektu?

Uwaga: Dla mnie pierwszy obiekt został skopiowany, nie przeniesiony, więcej o tym poniżej.

Odpowiedź: bezpieczeństwo wyjątków.

Objaśnienie: Podczas tego wektor odkrywa, że ​​ma pełny bufor (jeden) i musi utworzyć nowy bufor z miejscem dla dwóch osób. Tworzy nowy bufor. Następnie przenosi drugi obiekt do tego bufora (na końcu). Jeśli ta konstrukcja zgłasza wyjątek, oryginalny bufor pozostaje nienaruszony, a vector pozostaje niezmieniony. W przeciwnym razie elementy są przenoszone lub kopiowane z pierwszego bufora do drugiego bufora (w ten sposób przesuwają/kopiują pierwszy element drugi).

Jeśli A ma konstruktor ruchu noexcept, zostanie użyty move, aby przenieść go ze starego bufora do nowego. Jednak jeśli konstruktor ruchu nie jest noexcept, zostanie użyty copy. To znowu jest dla bezpieczeństwa wyjątkowego. Jeśli przejście od starego bufora do nowego może się nie powieść, stary bufor musi pozostać nienaruszony, aby można było przywrócić oryginalny stan vector.

Jeśli dodać noexcept do konstruktora ruch:

A(A && c) noexcept : j(c.j) 
{ 
    std::cout<<"move "<<j<<std::endl; 
} 

Wtedy mój wyjściowy:

A 
constructor 1 
move 1 
destructor 1 
B 
constructor 2 
move 2 
move 1 
destructor 1 // 2 
destructor 2 
C 
destructor 2 
destructor 1 

Uwaga linia oznaczona // 2 jest zniszczenie pierwszego elementu ze starego bufora, po został przeniesiony do nowego bufora.

  1. Dlaczego na końcu znajdują się dwa destruktory dla każdego obiektu?

to oznaczenie zniszczenie vector, a tym samym zniszczenia każdej z elementów, na który vector „S.

4

wektor jest zwiększenie jej pojemności i ruchomych elementów wewnętrznych podczas wywołania push_back.

5

legalne wykorzystywanie reserve rozwiązuje połowę problemu: http://ideone.com/5Lya6 poprzez ograniczenie liczby nieoczekiwanych ruchów (że nie jawnie życzenie)

też nie zapominać, że destruktor TEMP będzie nadal po ogień po przeniesieniu do wektora. Dlatego musisz upewnić się, że temperatura pozostaje w stanie rozsądnym, stanie zniszczalnym nawet po przeniesieniu/konstrukcji.

#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

Konstruktor ruchu nie "niszczy" poruszonego obiektu.

#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)); 
} 

output daje:

1 active 
1 inactive