Typ obiektu pochodnego

0

Witam,

class klasa1
{
	virtual void wirtualna() = 0;
};

class klasa2 : public klasa1
{};

class klasa3 : public klasa2
{
	void wirtualna(void){}
};

Mam trzy klasy. Tworzę obiekt klasy: klasa3.

klasa1 *k = new klasa3();

Operując samym wskaźnikiem potrzebuję się dobrać do typów obiektu na jaki pokazuje,
a dokładnie chcę znać jego nazwę.

cout << typeid(k).name() << endl;

Zaprezentowany wyżej kod zwraca mi nazwę: klasa1 - oczywista sprawa.
Jednak ja chcę mieć dostać również informacje o klasie: klasa2 i klasa3.
Domyślam się, że będzie konieczność użycia operatora: dynamic_cast.
Nie znoszę tych operatorów rzutowania (nie znoszę = nie mam dobrego źródła do ich nauki ;)).
Z góry dzięki.

0

Rozumiem że k = new klasa3()?
Jesteś pewien że włączyłeś RTTI w opcjach kompilatora?

0

Eee
musisz dać typeid(*k) a nie typeid(k)

0

Racja k = new klasa3() - przeedytowane.
A klase pośrednią: klasa2 analogicznie da się w jakiś sposób otrzymać?
Dzięki!

0

Twoj obiekt JEST typu klasa3 i kropka. Pytania o to, jakiego jest typu beda zwracac klasa3. To, że obiekt dziedziczy po klasa1 czy klasa2 nie zmienia jego typu. On jest klasa 3. Mozesz probowac slice'ować poprzez rzutowania na typ klasa2 czy klasa1, ale to nie koniecznie musi sie udac..

A typ wskaznika, czyli typ zmiennej ktora na niego pokazuje, jest zupelnie inna rzecza. Obiekt klasy3 moze byc wskazywany zarowno przez wskaznik-na-klase1 jak i przez wskaznik-na-klase2..

Co dokladnie chcesz uzyskac, hm? btw. nie przyzwyczajaj sie do tego .name() na typeid - to nie standardowa rzecz, dostepna tylko w Twoim typie kompilatora. NIE opieraj zadnych waznych wymyslnych mechanizmow na fakcie, ze jakies typeid().name() przypadkiem zwraca Ci nazwe typu obiektu

0

Nie miałem zamiaru opierać się w końcowej implementacji na tym typeid, bo chyba w Pasji czytałem, że to zła praktyka.. W sumie to nawet nie widzę potrzeby kiedy bym miał z tego korzystać, w końcu jak chcę coś porównywać w tym przypadku to wskaźniki.. Po prostu w ramach nauki, co gdzie jak się tworzy i jak się nazywa sobie wypisywałem i nic więcej.. Mam nadzieję, że to pod złą praktykę jeszcze nie zachacza ;). Podoba mi się stwierdzenie: "Twoj obiekt JEST typu klasa3 i kropka" - de facto do końca nie byłem pewien, ale już wszystko pięknie się układa.. W takim razie chcę uzyskać coś na kształt drzewa dziedziczenia, czyli nazwy klas po jakich klasa3 dziedziczy.

Chętnie również posłucham co to za mechanizm to slice'owanie i co mogę dzięki niemu osiągnąć.

0

slice to po prostu obcięcie obiektu pochodnego do obiektu bazowego. Jeśli przypiszesz obiektowi bazowemu obiekt pochodny to po prostu obetniesz go ze wszystkich informacji których typ bazowy nie przewiduje.

To jest troche dziwny problem, bo przecież ty wiesz po jakich klasach ktora dziedziczy. Dzięki temu próba pokazania wskaźnikiem na klasę A obiektu B w przypadku gdy B nie dziedziczy z A zakończy się blędem.

Jeśli chcesz się tak bawic w drugą stronę (tzn masz np. tablicę wskaźników do typu bazowego w której przechowujesz obiekty klas pochodnych, co jest częstą praktyką gdy stosujesz wywołania polimorficzne, i chciałbyś gdzieś sprawdzić czy pracujesz na obiekcie pewnej klasy pochodnej żeby wywołać szczególną dla tej klasy metodę) to możesz rzutować sobie wskaźnik / referencję w dół (w sensie w kierunku klasy pochodnej) za pomocą dynamic_cast i wtedy masz takie opcje:

  • albo dostaniesz nulla bo nie dało się zrzutować
  • albo dostaniesz wyjątek std::bad_cast bo nie dało się zrzutować
    (to jest kwestia czy rzutujesz wskaźnik czy referencje)
0

C++ (bazowy, bez CLI) w przeciwienstwie do Javy czy C# nie przewiduje czegos takiego jak reflection czyli po naszemu introspekcja, czyli odpytywanie obiektow/klas o strukturę ich zawartości. Bez ekstra narzedzi/frameworkow/(..) nie jestes w stanie -wprogramie- dowiedziec się jakie klasa ma metody, jakie metoda ma paametry, po jakich typach klasa dziedziczy, ba, nawet nie sprawdzisz jakiego typu jest zmienna/obiekt. Nie dasz rady napisac nic w stylu (pseudokod)

lista_metod = costam::pobierzmetody<klasa>();
for(...; lista_metod.size; ...) printf metoda.nazwa();

Na potrzeby polimorfizmu i dynamic_cast<> wprowadzono opcjonalny mechanizm o nazwie RTTI (RunTime Type Information), który zajmuje się próbą określenia co-jest-czym w sensie typów obiektów wskazywanych. Jeżeli go wyłączysz w opcjach kompilatora, wszystkie dynamic_cast zaczna nagle zwracac zero albo rzucać wyjątki - poza tym program bedzie działać i będzie mniejszy. Włączenie RTTI powoduje że kompilator wygenerowuje całą serie ukrytych, statycznych opisów klas, zawierających informację o ich drzewach dziedziczenia. Tutaj już masz pierwszy problem: z RTTI nie dowiedz się niczego o czymś co klasą/strukturą nie jest. RTTI musi miec mozliwosc przyczepienia do kazdego tworzonego obiektu jakiegos 'linka' do tych informacji - wybrano mniej/bardziej sprytną wczepkę do vtable - więc z RTTI nie dowiesz się niczego o czymś co jest 'niepolimorficzną' klasą, nie posiadającą składowych wirtualnych. Dostęp do RTTI w kodzie masz poprzez dwie konstrukcje: dynamic_cast<> oraz typeid(). Jak one dziełają - wiesz. I to koniec. C++ więcej 'wglądu' w faktyczny nieznany obiekt nie przewiduje. Mozesz więc kombinowac z RTTI tworzac hm potworki(?) w stylu:

//pseudokod
void drzewo(void* obiekt)
{
    if( dynamic_cast<klasaA1*>(obiekt) == 0 ) cout << "+klasaA1" << endl;
    if( dynamic_cast<klasaA2*>(obiekt) == 0 ) cout << "+klasaA2" << endl;
    if( dynamic_cast<klasaA3*>(obiekt) == 0 ) cout << "+klasaA3" << endl;

    if( dynamic_cast<klasaB1*>(obiekt) == 0 ) cout << "+klasaB1" << endl;
    if( dynamic_cast<klasaB2*>(obiekt) == 0 ) cout << "+klasaB2" << endl;
    if( dynamic_cast<klasaB3*>(obiekt) == 0 ) cout << "+klasaB3" << endl;

    if( dynamic_cast<klasaC1*>(obiekt) == 0 ) cout << "+klasaC1" << endl;
    if( dynamic_cast<klasaC2*>(obiekt) == 0 ) cout << "+klasaC2" << endl;
    if( dynamic_cast<klasaC3*>(obiekt) == 0 ) cout << "+klasaC3" << endl;
    ...
}

ale będzie bardzo ciężko Ci się dowiedziec, która klasa skladająca się na daną jest wyżej/niżej w drzewie. tym ciężej, że wszystkie typy musiałbyś wymieniać 'z palca', gdyż nie ma kogo ani jak odpytać o listę wszystkich znanych typów.. możesz kombinować cztając bezposrednio ze struktur RTTI - one w końcu zawierają dokladny opis dziedziczenia - jednak bedzie to dosc karkołomne..

Radziłbym więc Ci odejście od prób 'systemowych' i napisanie krótkiego, własnego rozwiązania:

//klasa bazowa
class Baza
{
...
public:    virtual std::string typeofMe() const { return "Baza"; }
...

}

class Inna : cokolwiek // cokolwiek dziedzicyz po Bazie
{
...
public:    virtual std::string typeofMe() const { return "Inna < " + cokolwiek::typeofMe(); }
...

}

itp.. tego typu proste kaskada pozwola Ci szybko zbadac cale potrzebne Ci drzewo. Nie sądzę, żebyś potrzebował więcej niż w/w.

0

Dzięki Shalom za info o slice'owaniu.

//pseudokod
void drzewo(void* obiekt)
{
    if( dynamic_cast<klasaA1*>(obiekt) == 0 ) cout << "+klasaA1" << endl;
    if( dynamic_cast<klasaA2*>(obiekt) == 0 ) cout << "+klasaA2" << endl;
    if( dynamic_cast<klasaA3*>(obiekt) == 0 ) cout << "+klasaA3" << endl;

    if( dynamic_cast<klasaB1*>(obiekt) == 0 ) cout << "+klasaB1" << endl;
    if( dynamic_cast<klasaB2*>(obiekt) == 0 ) cout << "+klasaB2" << endl;
    if( dynamic_cast<klasaB3*>(obiekt) == 0 ) cout << "+klasaB3" << endl;

    if( dynamic_cast<klasaC1*>(obiekt) == 0 ) cout << "+klasaC1" << endl;
    if( dynamic_cast<klasaC2*>(obiekt) == 0 ) cout << "+klasaC2" << endl;
    if( dynamic_cast<klasaC3*>(obiekt) == 0 ) cout << "+klasaC3" << endl;
    ...
}

Wiedziałem, że da się zrobić coś w tym stylu. Nie jest to złe rozwiązanie, chociaż wtedy rzeczywiście nie będę znał hierarchii. Myślałem też, że są gotowe mechanizmy do tego, ale jeśli nie ma to dobre i to. Opcja z implementacją funkcji typeOf całkiem sympatyczna, chociaż chciałbym, żeby ten mechanizm był uniwersalny dla wszystkich klas. Zaciekawiłeś mnie tymi strukturami RTTI. Jeśli dobierając się do nich będę sobie w stanie wyciągnąć dane do mojego drzewka, to chyba z tym powalczę, chociaż miałbyć to 'quest' poboczny, a się nie zapowiada. Pod C++/CLI nie pisałem jeszcze, chociaż korzystam z Visuala, może kiedyś przyjdzie na niego czas.. Rozumiem, że wtedy z wyżej opisanym problemem nie będę miał problemu?

0

Jeśli zaczniesz, pamietaj że RTTI ma informacje tylko o tym, co ma cokolwiek wirtualnego.
Tak, w C++/CLI istnieje jak to w .Net mechanizm reflection, ale -- żeby nie było tak dobrze - tylko dla typów zarządzanych, czyli tych .Netowych, oznaczonych przez 'ref'... ale, powiedzmy ze dla 'wszystkich' ktorych pewnie byś używał

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