Jak zaimplementować konstruktora kopiującego dla klasy, która ma unique_ptr
zmienną składową? Rozważam tylko C ++ 11.
c++
c++11
unique-ptr
codefx
źródło
źródło
std::vector
.unique_ptr
, prawdopodobnie potrzebujesz konstruktora przenoszenia, jeśli Twoim celem jest umieszczenie danych w plikustd::vector
. Z drugiej strony standard C ++ 11 automatycznie utworzył konstruktory przenoszenia, więc może potrzebujesz konstruktora kopiującego ...Odpowiedzi:
Ponieważ
unique_ptr
nie można udostępniać, musisz albo głęboko skopiować jego zawartość, albo przekonwertowaćunique_ptr
plikshared_ptr
.Możesz, jak wspomniano w NPE, użyć narzędzia do przenoszenia zamiast edytora kopii, ale spowodowałoby to inną semantykę twojej klasy. Operator ruchu musiałby uczynić członka jako ruchomym jawnie poprzez
std::move
:Posiadanie pełnego zestawu niezbędnych operatorów również prowadzi do
Jeśli chcesz użyć swojej klasy w a
std::vector
, po prostu musisz zdecydować, czy wektor będzie unikalnym właścicielem obiektu, w takim przypadku wystarczy uczynić klasę ruchomą, ale nie kopiowalną. Jeśli pominiesz program copy-ctor i copy-assignment, kompilator pokieruje Cię, jak używać std :: vector z typami tylko do przenoszenia.źródło
int
. Jeśli masz,unique_ptr<Base>
który przechowuje aDerived
, powyższe będzie cięło.A( const A& a ) : up_( a.up_ ? new int( *a.up_ ) : nullptr) {}
value_ptr
-unique_ptr
plus usuwającej / kopiującej.Typowym przypadkiem, w którym ktoś ma a
unique_ptr
w klasie, jest możliwość użycia dziedziczenia (w przeciwnym razie zwykły obiekt również często by działał, patrz RAII). W tym przypadku do tej pory nie ma odpowiedniej odpowiedzi w tym wątku .Oto punkt wyjścia:
... a celem jest, jak powiedziano, uczynienie go
Foo
możliwym do skopiowania.W tym celu należy wykonać głęboką kopię zawartego wskaźnika, aby zapewnić poprawne skopiowanie klasy pochodnej.
Można to osiągnąć, dodając następujący kod:
Zasadniczo mają tu miejsce dwie rzeczy:
Pierwszym z nich jest dodanie konstruktorów kopiowania i przenoszenia, które są niejawnie usuwane,
Foo
gdy konstruktor kopiującyunique_ptr
jest usuwany. Konstruktor przenoszenia można dodać po prostu przez= default
... co ma na celu tylko poinformowanie kompilatora, że zwykły konstruktor przenoszenia nie powinien zostać usunięty (działa to, ponieważunique_ptr
ma już konstruktor przenoszenia, którego można użyć w tym przypadku).W przypadku konstruktora kopiującego programu
Foo
nie ma podobnego mechanizmu, ponieważ nie ma konstruktora kopiującego programuunique_ptr
. Trzeba więc skonstruować nowyunique_ptr
, wypełnić go kopią oryginalnego pointee i użyć go jako członka skopiowanej klasy.W przypadku dziedziczenia kopia oryginalnego wskazanego musi być wykonana starannie. Powodem jest to, że wykonanie prostej kopii przez
std::unique_ptr<Base>(*ptr)
w powyższym kodzie skutkowałoby cięciem na plasterki, tj. Kopiowany byłby tylko podstawowy komponent obiektu, podczas gdy brakuje części pochodnej.Aby tego uniknąć, kopię należy wykonać za pomocą wzorca klonowania. Chodzi o to, aby wykonać kopię za pomocą funkcji wirtualnej,
clone_impl()
która zwracaBase*
w klasie bazowej. Jednak w klasie pochodnej jest on rozszerzany za pomocą kowariancji w celu zwrócenia aDerived*
, a ten wskaźnik wskazuje na nowo utworzoną kopię klasy pochodnej. Klasa bazowa może następnie uzyskać dostęp do tego nowego obiektu za pośrednictwem wskaźnika klasy bazowejBase*
, zawinąć go w aunique_ptr
i zwrócić za pośrednictwem rzeczywistejclone()
funkcji wywoływanej z zewnątrz.źródło
unique_ptr
podczas gdy bezpośrednie powstrzymanie zrobiłoby inaczej. Odpowiedź??? Dziedziczenie .unique_ptr
ponadoptional
dla pustych typów.clone_impl
in base, kompilator nie powie Ci, jeśli zapomnisz o tym w klasie pochodnej. Możesz jednak użyć innej klasy bazowejCloneable
i zaimplementować tam czystą wirtualnąclone_impl
. Wtedy kompilator będzie narzekał, jeśli zapomnisz o tym w klasie pochodnej.Wypróbuj tego pomocnika, aby tworzyć głębokie kopie i radzić sobie, gdy źródło unique_ptr ma wartość null.
Na przykład:
źródło
Daniel Frey wspomniał o rozwiązaniu do kopiowania, chciałbym porozmawiać o tym, jak przenieść unique_ptr
Nazywa się je konstruktorem przenoszenia i przypisaniem przenoszenia
możesz ich użyć w ten sposób
Musisz zawinąć a i c std :: move, ponieważ mają one nazwę std :: move mówi kompilatorowi, aby przekształcił wartość na odniesienie rvalue niezależnie od parametrów. W technicznym sensie std :: move jest analogią do czegoś takiego jak " std :: rvalue "
Po przeniesieniu zasób z unique_ptr jest przenoszony do innego unique_ptr
Istnieje wiele tematów, które dokumentują odniesienie do wartości r; jest to dość łatwe na początek .
Edytować :
Przeniesiony obiekt pozostanie ważny, ale stan nieokreślony .
C ++ primer 5, ch13 również daje bardzo dobre wyjaśnienie, jak „przesuwać” obiekt
źródło
a
po wywołaniu std :: move (a) wb
konstruktorze przenoszenia? Czy to jest po prostu całkowicie nieważne?Proponuję użyć make_unique
źródło
unique_ptr
nie można go kopiować, można go tylko przenosić.Wpłynie to bezpośrednio na Test, który w twoim drugim przykładzie jest również możliwy do przeniesienia i nie do skopiowania.
W rzeczywistości dobrze jest, gdy używasz,
unique_ptr
który chroni cię przed dużym błędem.Na przykład głównym problemem związanym z pierwszym kodem jest to, że wskaźnik nigdy nie jest usuwany, co jest naprawdę, bardzo złe. Powiedzmy, że możesz to naprawić przez:
To też jest złe. Co się stanie, jeśli skopiujesz
Test
? Będą dwie klasy, które będą miały wskaźnik wskazujący na ten sam adres.Kiedy jeden
Test
zostanie zniszczony, zniszczy również wskaźnik. Kiedy twoja sekundaTest
zostanie zniszczona, spróbuje również usunąć pamięć za wskaźnikiem. Ale został już usunięty i otrzymamy zły błąd w czasie wykonywania dostępu do pamięci (lub niezdefiniowane zachowanie, jeśli mamy pecha).Zatem właściwym sposobem jest albo zaimplementowanie konstruktora kopiującego, jak i operatora przypisania kopiującego, aby zachowanie było jasne i można było utworzyć kopię.
unique_ptr
wyprzedza nas tutaj. Ma znaczenie semantyczne: „ Jestemunique
, więc nie możesz mnie tak po prostu skopiować. ” Tak więc, zapobiega to błędowi implementacji dostępnych operatorów.Możesz zdefiniować konstruktora kopiującego i operatora przypisania kopii dla specjalnego zachowania, a kod będzie działał. Ale słusznie (!) Jesteś do tego zmuszony.
Morał tej historii: zawsze używaj
unique_ptr
w tego rodzaju sytuacjach.źródło