2013-05-28 11 views
6

Czytam wzory wzorów C++ i instrumentów pochodnych przez Marka Joshi i implementuję jego kod w C++ 11. Wszystko poszło całkiem nieźle, aż dotarłem do rozdziału 4, gdzie omawia konstruktorów kopii wirtualnych.Konstruktor wirtualnych kopii C++ 11

PayOffDoubleDigital thePayOff(Low, Up); 
VanillaOption theOption(thePayOff, Expiry); 

Problem polega na tym, że VanillaOption zawiera odniesienie do thePayOff. Jeśli tak jest i ktoś modyfikuje thePayOff, zachowanie theOption może zostać zmodyfikowane nieświadomie. Rozwiązanie radzi jest utworzenie konstruktora wirtualnego kopiowania w klasie bazowej PayOffDoubleDigital „s, PayOff tak że theOption zawiera własną kopię:

virtual PayOff* clone() const = 0; 

a następnie zdefiniowane w każdej dziedziczonej klasy:

PayOff* PayOffCall::clone() const 
{ 
    return new PayOffCall(*this); 
} 

Skrutacyjnej new złapał mnie jako coś, co może być nieodpowiednie w C++ 11. Jaki jest więc właściwy sposób obsługi tego przy użyciu C++ 11?

+3

Reguła zerowa: należy utworzyć klasę obsługi z odpowiednią semantyką własności i używać jej wszędzie. –

+0

Wygląda na to, że mam dodatkowe badania. Dziękuję Martinho. – BDig

+0

Pisałem o tym wcześniej: http://flamingdangerzone.com/cxx11/2012/08/15/rule-of-zero.html –

Odpowiedz

11

Rozwiązanie radzi jest stworzenie wirtualnego konstruktora kopii w klasie bazowej PayOffDoubleDigital za [...]

Przede wszystkim clone() nie jest kopią-konstruktor. Konstruktor kopia dla klasy X jest specjalną funkcją członkiem bez typ zwracany który zwykle ma podpis:

X(X const&) 

I może mieć podpis:

X(X&) 

funkcji clone() jest zwykłym (wirtualna), a jej szczególne znaczenie jest rozpoznawane przez ciebie - użytkownika - jako coś, co tworzy klony twojego obiektu, ale nie przez kompilator, który nie ma pojęcia, co robi clone().

Wracając Nowość złapał mnie za coś, co może być nieodpowiednie w C++ 11

To prawda, używając new nie jest idiomatyczne w C++ 11. W rzeczywistości, w C++ 11 powinieneś (prawie) nigdy nie używać new, chyba że robisz naprawdę niskopoziomowe zarządzanie pamięcią (coś, czego powinieneś unikać, chyba że musisz naprawdę) - oraz w C++ 14, możesz usunąć "prawie". Niestety, jest to prawdopodobnie wyjątkowy przypadek, w którym potrzebne jest new.

mówię to, ponieważ wierzę, zwracając unique_ptr brzmi właściwa rzecz do zrobienia (obiekt opcja musi posiadać swój własny PayOff obiekt, a które muszą pozostać przy życiu tak długo, jak długo żyje obiekt opcja) i nie ma std::make_unique() funkcja w C++ 11 (będzie tam w C++ 14):

std::unique_ptr<PayOff> PayOffCall::clone() const 
{ 
    return std::unique_ptr<PayOff>(new PayOffCall(*this)); 
} 

Mając VanillaOption (lub jego klasy bazowej) posiadają unique_ptr zamiast surowego wskaźnik uczyniłoby go niepotrzebny do delete obiektu PayOff zwrócony przez clone(). Z kolei nie musi to oznaczać, że obiekt nie musi definiować destruktora dostarczanego przez użytkownika, i nie ma potrzeby dbać o to, aby Rule of Three, Rule of Five, ani nic.

Ilekroć możesz, postępuj zgodnie z R. Martinho's Fernandes's advice i przejdź do Rule of Zero.

+0

Dziękuję za przemyślaną odpowiedź. To jest obszar C++, który był dla mnie trudny do opanowania. Zaznaczę to, jeśli będę mógł. Ale to jest moje pierwsze pytanie, nie mam niezbędnej reputacji. Potrzebujesz więcej ludzi, aby oznaczyć pytanie :) – BDig

+0

@BDig: Nie martw się, zadawaj dobre pytania, a wkrótce dostaniesz powtórzyć :) Cieszę się, że mogę pomóc, i powodzenia z twoją książką –

0

Podobnie jak w przypadku własności, najczystszym rozwiązaniem jest zwrócenie inteligentnego wskaźnika: zarówno gwarantuje bezpieczeństwo wyjątków (brak ryzyka wycieku pamięci), jak i wyjaśnia, kto jest właścicielem.

Niezależnie od tego, czy chcesz używać unique_ptr, czy shared_ptr zależy całkowicie od Ciebie.