Jaka jest najlepsza metoda przekazywania a shared_ptr
typu pochodnego do funkcji, która przyjmuje shared_ptr
typ podstawowy?
Generalnie przekazuję shared_ptr
je jako odniesienie, aby uniknąć niepotrzebnych kopii:
int foo(const shared_ptr<bar>& ptr);
ale to nie działa, jeśli spróbuję zrobić coś takiego
int foo(const shared_ptr<Base>& ptr);
...
shared_ptr<Derived> bar = make_shared<Derived>();
foo(bar);
mógłbym użyć
foo(dynamic_pointer_cast<Base, Derived>(bar));
ale wydaje się to nieoptymalne z dwóch powodów:
dynamic_cast
Wydaje się nieco nadmierne dla prostego pochodzi do podstawy obsady.- Jak rozumiem,
dynamic_pointer_cast
tworzy kopię (choć tymczasową) wskaźnika, aby przejść do funkcji.
Czy jest lepsze rozwiązanie?
Aktualizacja dla potomnych:
Okazało się, że był to problem z brakującym plikiem nagłówkowym. Poza tym to, co próbowałem tutaj zrobić, jest uważane za anty-wzór. Ogólnie,
Funkcje, które nie wpływają na czas życia obiektu (tj. Obiekt pozostaje ważny przez czas trwania funkcji) powinny przyjmować zwykłe odwołanie lub wskaźnik, np
int foo(bar& b)
.Funkcje, które konsumują obiekt (czyli są końcowymi użytkownikami danego obiektu) powinny przyjmować
unique_ptr
wartość, npint foo(unique_ptr<bar> b)
. Wzywający powinnistd::move
podać wartość do funkcji.Funkcje wydłużające żywotność obiektu powinny przyjmować
shared_ptr
wartość, npint foo(shared_ptr<bar> b)
. Obowiązuje zwyczajowa rada, aby unikać odwołań cyklicznych .
Szczegóły w artykule Herba Suttera Back to Basics .
źródło
shared_ptr
? Dlaczego brak odniesienia do stałej baru?dynamic
obsada jest potrzebna tylko do obniżenia. Również przekazanie wskaźnika pochodnego powinno działać dobrze.shared_ptr
Utworzy nowy z tym samym refcount (i zwiększy go) i wskaźnikiem do bazy, która następnie zostanie powiązana z referencją const. Ponieważ jednak już bierzesz referencje, nie rozumiem, dlaczegoshared_ptr
w ogóle chcesz je wziąć . WeźBase const&
i zadzwońfoo(*bar)
.foo(bar)
) Nie działa, przynajmniej w MSVC 2010.shared_ptr
aby przejść do funkcji? Jestem prawie pewien, że nie da się tego uniknąć.Odpowiedzi:
Chociaż
Base
iDerived
są kowariantne, a surowe wskaźniki do nich będą działać odpowiednioshared_ptr<Base>
i nieshared_ptr<Derived>
są kowariantne. Jest to poprawny i najprostszy sposób rozwiązania tego problemu.dynamic_pointer_cast
( Edycja:
static_pointer_cast
byłoby bardziej odpowiednie, ponieważ rzutujesz z wersji pochodnej do bazy, co jest bezpieczne i nie wymaga sprawdzania w czasie wykonywania. Zobacz komentarze poniżej).Jeśli jednak twoja
foo()
funkcja nie chce brać udziału w wydłużaniu czasu życia (a raczej brać udział we współwłasności obiektu), to najlepiej jest zaakceptować aconst Base&
i usunąć odniesienie doshared_ptr
podczas przekazywania gofoo()
.void foo(const Base& base); [...] shared_ptr<Derived> spDerived = getDerived(); foo(*spDerived);
Na marginesie, ponieważ
shared_ptr
typy nie mogą być kowariantne, reguły niejawnych konwersji w kowariantnych typach zwracanych nie mają zastosowania podczas zwracania typówshared_ptr<T>
.źródło
shared_ptr<Derived>
można je niejawnie konwertować nashared_ptr<Base>
, więc kod powinien działać bez rzucania shenaniganów.shared_ptr<Ty>
ma konstruktor, który pobiera ashared_ptr<Other>
i wykonuje odpowiednią konwersję, jeśliTy*
jest niejawnie konwertowany naOther*
. A jeśli potrzebna jest obsada,static_pointer_cast
to jest odpowiednia, a niedynamic_pointer_cast
.shared_ptr
odwołań do, aby uniknąć liczenia odwołań, to naprawdę nie ma dobrego powodu, aby używaćshared_ptr
w pierwszej kolejności. Najlepiejconst Base&
zamiast tego użyć .static_pointer_cast
, dzięki.Dzieje się tak również, jeśli zapomniałeś określić dziedziczenie publiczne w klasie pochodnej, tj. Jeśli tak jak ja napiszesz to:
class Derived : Base { };
źródło
class
dotyczy parametrów szablonu;struct
służy do definiowania klas. (To co najwyżej 45% żartu.)Wygląda na to, że za bardzo się starasz.
shared_ptr
jest tani w kopiowaniu; to jeden z jego celów. Przekazywanie ich przez odniesienie tak naprawdę niewiele daje. Jeśli nie chcesz udostępniać, przekaż nieprzetworzony wskaźnik.To powiedziawszy, są na to dwa sposoby, które przychodzą mi do głowy:
foo(shared_ptr<Base>(bar)); foo(static_pointer_cast<Base>(bar));
źródło
shared_ptr
implementację, którą dostarcza Microsoft.Sprawdź również, czy
#include
plik nagłówkowy zawierający pełną deklarację klasy pochodnej znajduje się w pliku źródłowym.Miałem ten problem. Nie
std::shared<derived>
chciał rzucaćstd::shared<base>
. Zadeklarowałem obie klasy, aby móc przechowywać do nich wskaźniki, ale ponieważ nie miałem#include
kompilatora, nie mogłem zobaczyć, że jedna klasa pochodzi od drugiej.źródło