2009-09-02 15 views
5

Czy inteligentne wskaźniki obsługują odlewanie w dół, a jeśli nie, to w jaki sposób można obejść to ograniczenie?W jaki sposób powinny być rzucane inteligentne wskaźniki?

Przykładem tego, co próbuję zrobić, jest posiadanie dwóch wektorów STL (na przykład) zawierających inteligentne wskaźniki. Pierwsza zawiera inteligentne wskaźniki do klasy bazowej, a druga zawiera inteligentne wskaźniki do klasy pochodnej. Inteligentne wskaźniki są indeksowane, np. podobne zachowanie do shared_ptr z Boost, ale ręcznie zwijane. Podaję przykładowy kod, który mam do bitej stanowić przykład:

vector<CBaseSmartPtr> vecBase; 
vector<CDerivedSmartPtr> vecDer; 
... 
CBaseSmartPtr first = vecBase.front(); 
vecDer.push_back(CDerivedSmartPtr(dynamic_cast<CDerived*>(first.get())); 

Wydaje nie bezpieczne do mnie, myślę, że kończy się dwa wskaźniki inteligentne zarządzanie tego samego obiektu. W pewnym momencie może to spowodować, że jeden z nich zwolni obiekt, podczas gdy drugi nadal będzie zawierał odniesienia do niego.

To, na co mam nadzieję, ale nie sądzę, że zadziała, to prosta obsada przy zachowaniu tego samego obiektu, np.

dynamic_cast<CDerivedSmartPtr>(first) 

powinienem szukać zmienić drugi pojemnik również użyć CBaseSmartPtr i przygnębiony tylko na wykorzystanie? Czy są inne rozwiązania?

+2

Czemu tworzyć własne? – GManNickG

+1

Myślę, że jest to interesujące pytanie, ponieważ dostaje się do wnętrza inteligentnych implementacji wskaźnika. Ale chciałbym również podkreślić, że budowanie downcastingu w twojej strategii oznacza wadę projektu, IMHO. –

+0

Aby odpowiedzieć na pytanie, dlaczego robię swoje własne, to starsze kody i wyrwanie bieżącego użycia i zastąpienie go Boost byłoby całkiem dużą zmianą. To jest na mojej liście "przyszłych rzeczy, na które warto spojrzeć". – dlanod

Odpowiedz

5

Inteligentne wskaźniki mogą obsługiwać downcasting, ale nie są automatyczne. A uzyskanie stałej poprawności może być nieco skomplikowane (użyłem naszej inteligentnej implementacji wskaźnika w pytaniach do wywiadu, jest w tym jakiś schemat oszustwa). Jednak wielu użytkowników inteligentnych wskaźników i tak nie tworzy ich inteligentnych wskaźników z typami o stałych wartościach.

Pierwszą rzeczą, którą musisz poprawić, jest licznik. Ponieważ może być konieczne udostępnienie licznika między smart_ptr<Base> i smart_ptr<Derived>, typ licznika nie powinien zależeć od argumentu typu. Ogólnie rzecz biorąc, to i tak nie jest wielka sprawa. Licznik to tylko size_t, prawdopodobnie zawarty w klasie. (Uwaga: istnieją alternatywne projekty inteligentnych wskaźników, ale pytanie zdecydowanie sugeruje użycie licznika)

Odlew w kierunku podstawy powinien być dość trywialny. W związku z tym twój smart_ptr powinien mieć konstruktora biorąc smart_ptr. W tym poleceniu dodaj linię static_cast<T*>((U*)0);. To nie generuje kodu, ale zapobiega powstawaniu, gdy T nie jest podstawą U (modulo const qualified).

Na odwrót powinna być wyraźna obsada. Nie można programowo wyliczyć wszystkich baz T, więc smart_ptr<T> nie może pochodzić z wersji smart_ptr<Base1_of_T>, smart_ptr<Base2_of_T>, .... W związku z tym dynamic_cast<smart_ptr<T> > nie będzie działać. Możesz podać własną smart_dynamic_cast<SPT>(smart_ptr<U> const& pU). Najlepiej jest to implementować jako funkcję przechwytującą SPT. W tej funkcji można po prostu zrobić return SPT(dynamic_cast<SPT::value_type*>(&*pU)).

+0

Właściwie, myślę, że nie można programowo wyliczyć baz. Czy Andrej nam nie pokazał, jak to zrobić? – sbi

+0

Mam książkę, ale nie tutaj. Nie pamiętam jednak czegoś takiego. Aby usunąć wszelkie pomyłki: Celem "wyliczania baz" jest ustalenie zbioru typów B1..Bn danego typu D, tak, że relacja 'is_base_and_derived ' obowiązuje dla wszystkich typów B1..Bn i żadnych innych typów. – MSalters

+0

@MSalters: Ah mój mózg. Myślałem o wyliczeniu typów i uczynieniu z nich klasy bazowej ... Przepraszam. – sbi

0

Normalne inteligentne wskaźniki, jak std::auto_ptr, nie są bezpieczne w użyciu w pojemnikach STL, ze względu na własności przeniesione są wokół gdy STL przypisuje przypadki inteligentnych wskaźników do siebie dane kopiuje wokół wewnętrznie. Musisz użyć czegoś takiego jak boost::shared_ptr, które wewnętrznie implementuje liczenie odwołań, aby zapewnić, że obiekt pozostanie przy życiu niezależnie od tego, ile instancji inteligentnego wskaźnika go odsyła. Jeśli piszesz własne typy inteligentnych wskaźników, musisz zaimplementować podobne liczenie odwołań.

+0

Pozdrawiam, dodam komentarz do tego pytania, wyraźnie zaznaczając, że planuję użyć referencyjnych inteligentnych wskaźników. – dlanod

2

Właściwość, której chcesz użyć, to kowariancja we wskazanym typie. Oznacza to, że jeśli D jest B, to chcesz smartptr<D> isa smartptr<B>. Nie sądzę, że jest to w ogóle obsługiwane w C++, ale jak zwykle dostępne są szablony/przeciążenia.

http://www.boost.org/doc/libs/1_39_0/libs/smart_ptr/pointer_cast.html daje dynamiczną obsadę, która działa na regularnych i boost :: smart_ptr. Powinieneś uczyć się od implementacji, jeśli nie chcesz używać Boost's.

+4

+1 dla 'dynamic_pointer_cast' i -1 dla mówiącego C++ nie ma kowariantnych typów zwracanych. Zobacz tutaj: http://www.parashift.com/c++-faq-lite/virtual-functions.html#faq-20.8 –

+0

@Kristo: Jestem z wami oboma, ale nadal uważam, że to bardzo dobra rada. @ Wang-Wang: Dlaczego nie usuniesz fałszywego oświadczenia? Ułatwiłoby to innym przyjęcie tej, w przeciwnym razie dobrej, odpowiedzi. – sbi

+0

Masz rację - nie zdawałem sobie sprawy, że metody wirtualne pozwalają na typy kowariancyjne! Ale prawdą jest, że typy ogólne, np. ptr wektor nie obsługuje kowariancji (podobnie jak w Javie, chociaż po typie wymazania). –

1

Śledź wątek here na jednej z list mailingowych doładowania. Pokazuje, jak można zastosować downcasting inteligentnego wskaźnika w przypadku boost :: shared_ptr. HTH

+0

To wygląda wręcz przerażająco. Rzuca plik shared_ptr * na shared_ptr *. Zobacz także odpowiedź Rainera Deykego. – MSalters

+0

Hmmm, tak. Dziękuję za aktualizację. – Abhay

0

Znalazłem to na stronach Microsoft:

std::shared_ptr<base> sp0(new derived); 
    std::shared_ptr<derived> sp1 = 
    std::dynamic_pointer_cast<derived>(sp0); 
Powiązane problemy