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";
}
- Mam usunięte
const
od konstruktora przenieść.
- Usunąłem
std::move
z push_back
(jest zbędne).
- 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
- 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
.
- 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.
- 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.
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