Jestem dość zdezorientowany ze dynamic_cast
słowem kluczowym w C ++.
struct A {
virtual void f() { }
};
struct B : public A { };
struct C { };
void f () {
A a;
B b;
A* ap = &b;
B* b1 = dynamic_cast<B*> (&a); // NULL, because 'a' is not a 'B'
B* b2 = dynamic_cast<B*> (ap); // 'b'
C* c = dynamic_cast<C*> (ap); // NULL.
A& ar = dynamic_cast<A&> (*ap); // Ok.
B& br = dynamic_cast<B&> (*ap); // Ok.
C& cr = dynamic_cast<C&> (*ap); // std::bad_cast
}
definicja mówi:
dynamic_cast
Kluczowe rzuca punkt odniesienia z jednego wskaźnika lub odniesienia do innego typu, przeprowadza kontrolę wykonania w celu zapewnienia ważności obsady
Czy możemy napisać odpowiednik dynamic_cast
C ++ w C, abym mógł lepiej zrozumieć rzeczy?
c++
dynamic-cast
Vijay
źródło
źródło
dynamic_cast<>
działa za kulisami (lub jak dużo C ++ działa), dobrą książką (która jest również dość łatwa do przeczytania w przypadku czegoś tak technicznego) jest „Inside the C ++ Object Model” Lippmana. Również książki Stroustrupa „Design and Evolution of C ++” i „The C ++ Programming Language” są dobrymi zasobami, ale książka Lippman jest poświęcona temu, jak C ++ działa „za kulisami”.B* b2 = dynamic_cast<B*> (ap) // 'b'
?b2 is pointer to b
albo co?Odpowiedzi:
Oto podsumowanie,
static_cast<>
adynamic_cast<>
konkretnie, jak odnoszą się one do wskaźników. To tylko lista 101 poziomów, nie obejmuje wszystkich zawiłości.static_cast <Type *> (ptr)
To pobiera wskaźnik
ptr
i próbuje bezpiecznie rzutować go na wskaźnik typuType*
. To rzutowanie jest wykonywane w czasie kompilacji. Wykonuje rzutowanie tylko wtedy, gdy typy typów są powiązane. Jeśli typy nie są powiązane, pojawi się błąd kompilatora. Na przykład:dynamic_cast <Type *> (ptr)
To ponownie próbuje wziąć wskaźnik
ptr
i bezpiecznie rzucić go na wskaźnik typuType*
. Ale to rzutowanie jest wykonywane w czasie wykonywania, a nie w czasie kompilacji. Ponieważ jest to rzutowanie w czasie wykonywania, jest przydatne zwłaszcza w połączeniu z klasami polimorficznymi. W rzeczywistości w pewnych przypadkach klasy muszą być polimorficzne, aby obsada była legalna.Rzuty mogą przebiegać w jednym z dwóch kierunków: od podstawy do pochodnej (B2D) lub od pochodnej do podstawy (D2B). Wystarczy zobaczyć, jak rzutowanie D2B będzie działać w czasie wykonywania. Albo
ptr
pochodzi z,Type
albo nie. W przypadku D2B dynamic_cast <> s, zasady są proste. Możesz spróbować rzucić cokolwiek na cokolwiek innego, a jeśliptr
faktycznie zostało wyprowadzone zType
, otrzymaszType*
wskaźnik z powrotemdynamic_cast
. W przeciwnym razie otrzymasz wskaźnik NULL.Ale rzuty B2D są trochę bardziej skomplikowane. Rozważ następujący kod:
main()
nie można powiedzieć, jaki rodzaj obiektuCreateRandom()
zwróci, więc rzutowanie w stylu CBar* bar = (Bar*)base;
zdecydowanie nie jest bezpieczne dla typów. Jak mogłeś to naprawić? Jednym ze sposobów byłoby dodać funkcję jak boolAreYouABar() const = 0;
do klasy bazowej i powróttrue
zBar
ifalse
zFoo
. Ale jest inny sposób: użyjdynamic_cast<>
:Rzuty są wykonywane w czasie wykonywania i działają poprzez odpytywanie obiektu (na razie nie trzeba się martwić, jak to zrobić), pytając, czy jest to typ, którego szukamy. Jeśli tak,
dynamic_cast<Type*>
zwraca wskaźnik; w przeciwnym razie zwraca NULL.Aby to rzutowanie z bazy na wyprowadzenie działało przy użyciu
dynamic_cast<>
, Base, Foo i Bar muszą być tym, co Standard nazywa typami polimorficznymi . Aby być typem polimorficznym, Twoja klasa musi mieć co najmniej jednąvirtual
funkcję. Jeśli Twoje klasy nie są typami polimorficznymi, użycie metody bazowej na pochodnedynamic_cast
nie zostanie skompilowane. Przykład:Dodanie funkcji wirtualnej do bazy, takiej jak wirtualny dtor, spowoduje utworzenie polimorficznych typów Base i Der:
źródło
Base* base = new Base;
,dynamic_cast<Foo*>(base)
będzieNULL
.dynamic_cast<Foo*>(base)
jest null w przypadkuBase* base = new Base;
?base
nie jestFoo
.Base
Wskaźnik może wskazywaćFoo
, ale to wciążFoo
tak oddanych dynamiczne będą działać. Jeśli to zrobiszBase* base = new Base
,base
jest aBase
, a nie aFoo
, więc nie możesz dynamicznie rzutować go na plikFoo
.Jeśli nie zaimplementujesz własnego, ręcznie rozwijanego RTTI (i pominiesz systemowy), nie jest możliwe zaimplementowanie
dynamic_cast
bezpośrednio w kodzie C ++ na poziomie użytkownika.dynamic_cast
jest bardzo mocno powiązany z systemem RTTI implementacji C ++.Ale aby pomóc ci zrozumieć RTTI (a tym samym
dynamic_cast
) więcej, powinieneś przeczytać<typeinfo>
nagłówek itypeid
operatora. To zwraca informacje o typie odpowiadające obiektowi, który masz pod ręką, i możesz zapytać o różne (ograniczone) rzeczy z tych obiektów informacyjnych typu.źródło
dynamic_cast
są bardzo skąpe. :-P Po prostu baw się z tym sam, aż to zrozumiesz. :-)Więcej niż kod w C, myślę, że wystarczyłaby angielska definicja:
Biorąc pod uwagę klasę Base, w której istnieje klasa pochodna Derived,
dynamic_cast
konwertuje wskaźnik Base na wskaźnik Derived wtedy i tylko wtedy, gdy wskazany obiekt jest w rzeczywistości obiektem pochodnym.W tym przykładzie wywołanie
test
wiąże różne obiekty z odwołaniem doBase
. Wewnętrznie odwołanie jest redukowane do odwołania doDerived
w sposób bezpieczny dla typów: downcast zakończy się sukcesem tylko w tych przypadkach, w których obiekt, do którego istnieje odwołanie, jest rzeczywiście wystąpieniemDerived
.źródło
Poniższe nie są zbyt bliskie temu, co otrzymujesz z C ++
dynamic_cast
w zakresie sprawdzania typów, ale może pomoże ci trochę lepiej zrozumieć jego cel:źródło
A
dynamic_cast
przeprowadza kontrolę typu przy użyciu RTTI . Jeśli się nie powiedzie, zgłosi wyjątek (jeśli dałeś mu odniesienie) lub NULL, jeśli dałeś mu wskaźnik.źródło
Po pierwsze, aby opisać rzutowanie dynamiczne w języku C, musimy przedstawić klasy w języku C. Klasy z funkcjami wirtualnymi używają „TABELI VTABLE” wskaźników do funkcji wirtualnych. Komentarze są w C ++. Zapraszam do ponownego formatowania i naprawiania błędów kompilacji ...
Wtedy dynamiczna obsada to coś takiego:
źródło
W C nie ma klas, więc nie można napisać dynamic_cast w tym języku. Struktury C nie mają metod (w rezultacie nie mają metod wirtualnych), więc nie ma w tym nic „dynamicznego”.
źródło
Nie, niełatwo. Kompilator przypisuje każdej klasie unikalną tożsamość, do informacji odwołuje się każda instancja obiektu i właśnie to jest sprawdzane w czasie wykonywania w celu ustalenia, czy rzutowanie dynamiczne jest legalne. Możesz utworzyć standardową klasę bazową z tymi informacjami i operatorami, aby przeprowadzić inspekcję w czasie wykonywania tej klasy bazowej, wtedy każda klasa pochodna poinformuje klasę bazową o swoim miejscu w hierarchii klas, a wszelkie wystąpienia tych klas będą rzutowane w czasie wykonywania za pośrednictwem Twoje operacje.
edytować
Oto implementacja, która demonstruje jedną technikę. Nie twierdzę, że kompilator używa czegoś takiego, ale myślę, że demonstruje koncepcje:
źródło
dynamic_cast używa RTTI. Może spowolnić twoją aplikację, możesz użyć modyfikacji wzorca projektowego gościa, aby osiągnąć obniżenie bez RTTI http://arturx64.github.io/programming-world/2016/02/06/lazy-visitor.html
źródło
static_cast< Type* >(ptr)
static_cast w C ++ może być używany w scenariuszach, w których rzutowanie wszystkich typów można zweryfikować w czasie kompilacji .
dynamic_cast< Type* >(ptr)
Dynamic_cast w C ++ może służyć do wykonywania bezpiecznego rzutowania typu . dynamic_cast to polimorfizm w czasie wykonywania. Operator dynamic_cast, który bezpiecznie konwertuje ze wskaźnika (lub odwołania) do typu podstawowego na wskaźnik (lub odwołanie) do typu pochodnego.
np. 1:
Aby uzyskać więcej informacji, kliknij tutaj
np. 2:
źródło