2015-03-18 33 views
5

Potrzebuję napisać konstruktor kopii, który również przeniesie prawo własności do elementu unique_ptr kopiowanego obiektu. Sytuacja jest następująca:Kopiuj Konstruktor, aby przenieść własność unique_ptr

class C{ 
    // C class stuff  
}; 

class A{ 
public: 
    public A(); 
    public A(const A& a); 
private: 
    std::unique_ptr<C> c_; 
} 

class B{ 

public: 

    B(const A& b) : a_(a){} 

private: 
    A a_; 


}; 

Jak należy zaimplementować konstruktor kopiujący dla A?

+10

Więc kiedy tworzysz kopię, chcesz unieważnić oryginał? Sugerowałbym usunięcie konstruktora kopiowania i uczynienie klasy ruchomą tylko. – NathanOliver

+0

Jeśli z oryginałem masz na myśli oryginalny obiekt A yes, to powinno być "przeniesienieref" jako członek klasy B. Czy możesz podać przykład tego? – gcswoosh

+3

@Gabrielecswoosh Poświęć trochę czasu na czytanie pytań/odpowiedzi na znaczniku [tag: move-semantics]. –

Odpowiedz

5

Twoja intencja lub podejście jest złe, tak myślę.

Konstruktor kopii ma na celu utworzenie kopii argumentu, ale ponieważ unique_ptr zachowuje wyłączną własność, nie można jej skopiować. Ty, , możesz w rzeczywistości uczynić element unique_ptr mutable, a następnie przenieść zasób, na który wskazuje on w konstruktorze kopii, ale to byłoby absolutnie szalone (tak robi std::auto_ptr i dlatego jest przestarzałe).

Dlatego albo trzeba:

  • dodać move-konstruktora do A i B (+ uczynić kopiowaniem konstruktor skreślony)
  • przełącznik z unique_ptr do shared_ptr, ale tylko wtedy, gdy C jest rzeczywiście przeznaczona do być udostępniane

Istnieje również trzecia opcja, która jest, aby kopia obiektu wskazywanego przez unique_ptr w A za kopiowaniem konstruktora, czyli:

A::A(const A& a) : c_(std::unique_ptr<C>(a.c_ ? new C(*a.c_) : nullptr)) { 
} 
+0

Nie nazwałbym intencji źle. Całkiem dobrze jest stworzyć interfejs modelujący * zwykły typ *, którego typ zawijany nie może naturalnie wystawiać. – mavam

+0

Nie zgadzam się z tobą. możesz skopiować obiekt zawierający unique_ptr. na przykład mógłbym zaimplementować klasę LinkedList, która przechowuje unique_ptr jako wskaźnik dla następnego elementu. Czy to oznacza, że ​​nie mogę skopiować tej listy? Mogę skopiować samą listę i zapisać tę kopię w unique_ptr skopiowanego obiektu –

+4

@DavidHaim, ale OP chce przenieść własność w copy-constructor - tak mówi tytuł pytania. Uważam to za niewłaściwe podejście. –

3

Technicznie do

napisać konstruktor kopiujący, który również przeniesienie własności z unique_ptr członek obiektu skopiowany

można po prostu to zrobić:

class Bad 
{ 
private: 
    unique_ptr<int> p_; 
public: 
    Bad(): p_(new int(666)) {} 
    Bad(Bad& other) 
     : p_(move(other.p_)) 
    {} 
}; 

Ponieważ konstruktor kopii może mieć również ten podpis, plu s dwa więcej, oprócz bardziej konwencjonalnego Bad(const Bad&).

Nazwę tę klasę Bad, ponieważ jest naprawdę zła, po prostu nie ma sensu robić tej rzeczy, z wyjątkiem sabotażu czyjegoś kodu.

Zamiast konstruktora kopiującego, który nie kopiuje,

  • zaimplementować konstruktor pojedynek porusza lub

  • wdrożenia zwykłego kopiowania konstruktor kopie lub

  • zmienić projekt klasy na np. własność wspólna.

4

Oczywiście nie można po prostu wykonać przypisania std::unique_ptr s, ponieważ ich operator przypisania został usunięty. Jest to zamierzone, aby zmusić programistę do zdefiniowania zachowania, które chce.

  1. Nowy przedmiot przejmuje na własność c_, unieważniając oryginalny przedmiot.
  2. Nowy element tworzy kopię c_, zachowując ważność oryginalnych elementów.
  3. Nowa pozycja dzieli własność na c_, dzięki czemu zarówno nowe, jak i oryginalne elementy odnoszą się do tego samego obiektu.

W przypadek 1 co szukasz jest konstruktorem ruch, a domyślny konstruktor ruch będzie działać prawidłowo. Więc nie trzeba pisać żadnego kodu, można po prostu zrobić:

A temp; 
A foo(std::move(temp)); 

Zauważ, że temp jest nieprawidłowy po jego przeniesienie.

W przypadek 2 trzeba dodać konstruktora zwyczaj kopiowania do A aby utworzyć kopię oryginału c_:

A(const A& a):c_(new C(*(a.c_))){} 

Po zdefiniowaniu tego w A można zrobić:

A foo(A()); 

Należy pamiętać, że zależy to od funkcjonalności konstruktora kopiowania C.

W przypadek 3 trzeba zasadniczo zmienić A z użyciem std::unique_ptr do korzystania z std::shared_ptr, więc definicja c_ staną:

std::shared_ptr<C> c_; 

Twój budowa c_ byłyby identyczne co już używasz dla wersji std::unique_ptr z. Więc po prostu przy użyciu domyślnych implementacje można zrobić:

A foo; 
A bar(foo); 

a teraz foo i bar wskazują na ten sam C obiektu i podziel go na własność. Ten obiekt współdzielony nie zostanie usunięty, dopóki wszystkie odniesienia do niego nie zostaną usunięte.