Dziedziczenie, polimorfizm

0

Witam.

class Klasa1
{
};

class Klasa2 : public Klasa1
{
    Klasa2() : zmienna(0){}
    int zmienna;
};

Robię takie rzutowanie i odwołuję się do zmiennej.

Klasa2 klasa2;
Klasa1 *wsk = &klasa2;
Klasa2 *wsk2 = reinterpret_cast<Klasa2*>(wsk);
cout << wsk->zmienna; // Śmieci
Klasa2 klasa2;
void *wsk =&klasa2;
Klasa2 *wsk2 = reinterpret_cast<Klasa2*>(wsk);
cout << wsk->zmienna; // jest ok

Dlaczego dzieje się tak, że za pierwszym razem jest źle, a za drugim ok? Co złego jest w pierwszym rzutowaniu?

1

użyj static_cast a nie reinterpret_cast!

0

Dlaczego static_cast, a nie reinterpret_cast? W końcu to on jest do wskaźników.

1

Założę się, że twój prawdziwy kod (a nie ten prosty przykład), jest nieco bardziej złożony. W tak prostym przykładzie jak podałeś powyżej nie powinno być różnicy pomiędzy staic_cast i reinterpret_cast. W innych przypadkach różnica jest duża. Prawdopodobnie w twoim właściwym kodzie masz dziedziczenie wielokrotne lub wirtualne.

Generalnie jeśli nie wiesz na czym polega ta różnica to powinieneś stosować wersję static_cast. Tej drugiej praktycznie nie powinno się używać, a jeśli już to tylko i wyłącznie z pełną świadomością. Nie będę się wysilać na czym polega różnica, bo nie jest to proste do zrozumienia i zapewne bardziej ci zagmatwam niż wyjaśnię.

0

Tak, w moim kodzie klasa1 dziedziczy jeszcze po slist z __gnu_cxx. Może jednak spróbujesz?

3

Za pomocą reinterpret_cast możesz sobie konwertować na przykład char* na double*.
Program jedynie zinterpretuje podany adres jako inny adres właściwie nie wykonując żadnej operacji kodu maszynowego.
Czyli reinterpret_cast właściwie niczego nie konwertuje, kompilator jedyne przyjmuje na wiarę (często nie słusznie) że programista nie jest idiotą i wie co robi.

Jeżeli chcesz konwertować pomiędzy sobą wskaźniki klass będących w relacji bazowa - pochodna to właściwie w większości przypadków stosowane jest static_cast i jest do tego wystarczające.

Jeżeli zaś zachodzi dziedziczenie wielobazowe i/lub wirtualne to wtedy konieczne jest dynamic_cast (wymaga posiadania przynajmniej jednej metody wirtualnej, przez klasę bazową oraz klasę pochodną)

2
_13th_Dragon napisał(a)

Jeżeli zaś zachodzi dziedziczenie wielobazowe i/lub wirtualne to wtedy konieczne jest dynamic_cast (wymaga posiadania przynajmniej jednej metody wirtualnej, przez klasę bazową oraz klasę pochodną)

dynamic_cast jest potrzebne tylko wtedy, gdy w danej fazie programu nie masz pewności co do rzutowanego typu i chcesz dokonać jego sprawdzenia w trakcie konwersji.

AdamMC napisał(a)

Tak, w moim kodzie klasa1 dziedziczy jeszcze po slist z __gnu_cxx. Może jednak spróbujesz?

Skąd się bierze różnica między static_cast a reinterpret_cast?
Jeśli masz dziedziczenie wielobazowe, to w przypadku przypisania adresu obiektu do wskaźnika na typ bazowy, zapamietywany jest nie wskaźnik do całej struktury, ale wskaźnik do odpowiedniego fragmentu klasy, więc binarna wartość wskaźnika może niejawne otrzymać odpowiednie przesunięcie (offset).
Takie rozwiązanie jest konieczne by za pomocą tego wskaźnika prawidłowo obsługiwać inne klasy, które również rozszerzają tą klasę bazową i typ bazowy jest umieszczony w pamięci obiektu o innym typie w nico innym miejscu (offset).
W momencie użycia static_cast te dodatkowe przesuniecie jest brane pod uwagę i odwracane, wiec konwersja zachodzi prawidłowo, w przypadku reinterpret_cast adres interpretowany jest dosłownie i przesunięcie to jest ignorowane (jeśli istnieje takie przesuniecie prowadzi to do błędu).

W momencie, gdy przypisałeś wskaźnik na typ void* zapamiętujesz wskaźnik do całości klasy (zapominając jedynie o pierwotnym typie), więc reinterpret_cast zadziałało prawidłowo.

0
MarekR22 napisał(a)

dynamic_cast jest potrzebne tylko wtedy, gdy w danej fazie programu nie masz pewności co do rzutowanego typu i chcesz dokonać jego sprawdzenia w trakcie konwersji.

Jeżeli nie masz pewności to coś pochrzaniono w modelu. Ponieważ w przypadku polimorfizmu zawsze można sprawdzić przez typeid().

MarekR22 napisał(a)

Jeśli masz dziedziczenie wielobazowe, to w przypadku przypisania adresu obiektu do wskaźnika na typ bazowy, zapamietywany jest nie wskaźnik do całej struktury, ale wskaźnik do odpowiedniego fragmentu klasy, więc binarna wartość wskaźnika może niejawne otrzymać odpowiednie przesunięcie (offset).

A tu właśnie podajesz poprawną przyczynę użycia dynamic_cast. Czyli gdy obiekt pochodny dziedziczy dwa razy (lub też więcej) po np obiekcie Baza. W jakiś sposób uzyskałeś wskaźnik lub referencję do jednej z tych odziedziczonych obiektów Baza, niezależnie od tego na którą z nich będziesz mieć ten wskaźnik/referencję za pomocą dynamic_cast uzyskasz poprawny wskaźnik/referencję na obiekt pochodny.

1 użytkowników online, w tym zalogowanych: 0, gości: 1