Miałem na myśli kopiowanie podanego mu argumentu.
Ok, t tej sprawie zwracam honor. Nie zoruzmiałem
Inteligentny wskaźnik ma zająć się automatycznym zwolnieniem podanego mu wskaźnika. I tyle, to jest jego podstawowa rola. Ty dodatkowo chcesz dodać mu funkcjonalność sklonowania danego mu obiektu. Spoko, można to zrobić, ale z perspektywy inteligentnego wskaźnika to lukier składniowy i nie powinno go to za bardzo obchodzić. Koniec końców, on tylko zarządza swoim wskaźnikiem: czy będzie to wskaźnik zaalokowany wcześniej przez ciebie czy kopia obiektu - nie ma to absolutnie żadnego znaczenia. Można nawet powiedzieć, że łamie to single responsibility principle.
No i tu się myślisz. Dla takiego wskaźnika owo klonowanie jest niezbędne. Chyba nie rozumiesz w jakich przypadkach taki wskaźnik znajduje zastosowanie więc pozwól, że spróbuję przedstawić przykład. Będzie trochę Javowy, ale nie szkodzi. Weźmy abstrakcyjną klasę Container<T> i dziedziczące po nim Set<T> i List<T>. Implementują one metodę
Iterator<T> insert(const T& x);
która zwraca iterator na dodany do kontenera element. Ponieważ Set i List używają zupełnie inaczej działających iteratorów, zatem Iterator<T> też jest klasą abstrakcyjną, po której dziedziczy IteratorList<T> i IteratorSet<T>. Skoro tak to nie możemy Iteratora zwracać przez wartość. Musimy zrobić tak:
Iterator<T>* insert(const T& x);
Ale Iterator musi być dynamicznie tworzony wewnątrz metody insert. To sprawia, że użytkownik metody ma obowiązek zwolnić pamięć po iteratorze nawet gdy wcale go nie potrzebował. I tu w grę wchodzi inteligentny wskaźnik! Mamy taką metodę insert:
Pointer<Iterator<T> > insert(const T& x);
Teraz niezależnie od tego czy nas iterator interesuje czy nie, nie musimy się martwić o to, że była wewnątrz inserta dynamicznie zaalokowana pamięć.
A jeśli nas ten iterator interesuje:
Pointer<Iterator<T> > it = insert(x);
To zauważ, że niezbędny jest konstruktor kopiujący dla wskaźnika, który KLONUJE obiekt. Bo jakby go nie klonował tylko przepisywał adres to dwa wskaźniku rządziły by jednym obiektem i oba by go chciały usunąć.
W takim przypadku przy kopiowaniu takiego wskaźnika NIE MOŻNA bezmyślnie skopiować adresu, bo wskaźnik jest "właścicielem obiektu" i w wyniku dwa wskaźniki usuną ten sam obiekt co będzie katastrofą.
I dlatego po przekazaniu wskaźnika do inteligentnego wskaźnika to ty powinieneś uważać, żeby go nie zwolnić. Nie ma tutaj żadnej magicznej bariery, która cię przed takim głupstwem ochroni.
No i właśnie ja powinienem uważać, ale konstruktor kopiujący wskaźnika również musi zadbać oto by dwa wskaźniki nie były właścicielami tego samego obiektu.
I wiem, że nie ma ochrony przed głupstwem usunięcia obiektu samemu. Ale mam nadzieję, że teraz widzisz, że są jeszcze inne niebezpieczeństwa.
Co do skopiowania samego inteligentnego wskaźnika - tutaj jest prościej, bo możemy zwyczajnie wyłączyć możliwość jego kopiowania (ustawiając widoczność konstruktora kopiującego na private) albo użyć move semantics (a z referencjami rvalue z C++11 jeszcze się to upraszcza).
Na przedstawionym przykładzie widać, że wcale nie możemy.
No, ale widzę, że uparłeś się tego klonowania obiektu.
Ja się na nic nie uparłem. To jest znany wzorzec. Inteligentny wskaźnik tego typu korzysta z wzorca 'prototyp' by móc klonować obiekty.
@vpiotr
- jestem za pierwszym użyciem - jest bardziej tradycyjne, naturalne. W drugim nie wiadomo o co chodzi, dodatkowo następuje w nim kopiowanie.
Pierwsza sensowna odpowiedź w tym temacie.
-
@Platyna: nie wiem co chcesz osiągnąć, ale zainteresuj się boost::intrusive_ptr
Przedstawiony przykład powinien rozwiać wątpliwości co ciałem osiągnąć. Oczywiście był to tylko przykład i proszę mi nie wyjeżdżać zaraz z tekstami, że bez sensu jest takie dziedziczenie kontenerów, bo to wiem. Nie mogłem nic lepszego wymyślić na szybko. :)