C++ Dziedziczenie, pozyskanie instancji obiektu pochodnego

0

Witam, mam taki problem designerski, otóż mamy 2 (lub więcej) klasy pochodne D1, D2 oraz 1 klasę bazową B.
Zakładamy, że dysponujemy wskaźnikiem do obiektu bazowego.
Czy istnieje jakiś wzorzec projektowy lub sposób pozwalający w prosty sposób uzyskać wskaźnik do obiektu pochodnego ?
Uprzedzam, że dynamic cast nie jest rozwiązaniem mojego problemu.
Do tej pory wymyśliłem coś takiego:

 

class D1;
class D2;

class B{
   virtual D1 * getD1() { return NULL; }
   virtual D2 * getD2() { return NULL; }
};

class D1 : public B{
   D1 * getD1() { return this; }
};

class D2 : public B{
   D2 * getD2() { return this; }
};

//bla bla bla tutaj mamy jakiś wskaźnik na B powiedzmy ptr i robimy:

if(ptr->getD1() != NULL){
   D1 * ptrToD1 = ptr->getD1();
   ptrToD1->jakas_metoda_z_charakterystyczna_dla_D1();
}
else{
   D2 * ptrToD2 = ptr->getD2();
   ptrToD2->jakas_metoda_z_charakterystyczna_dla_D2();
}

ale nie wygląda mi to na dobre rozwiązanie...
Ostatecznie proszę o wytłumaczenie dlaczego dynamic_cast a nie takie rozwiązanie (lub na odwrót), ale nie ukrywam, że liczę na sprytniejsze rozwiązanie :).

Z góry dzięki za pomoc.

0

Ale to jest w ogóle bez sensu. Po co chcesz tak zrobić? Jeśli faktycznie konieczne jest dla ciebie sprawdzanie z jakim typem obiektu masz do czynienia to znaczy że problem designerski to ty masz gdzieś znacznie wcześniej... Poza tym nie bardzo rozumiem czemu dynamic_cast ci nie pasuje, skoro robi dokładnie to co chciałeś bez tworzenia idiotycznych metod.

0

To ze problem designerski mam to wiem i dlatego pisze.
Używanie dynamic_casta też świadczy o złym designie (powiedzmy jest to dla mnie ostateczność).
Definiując problem inaczej:
Mamy różne elementy, które chcemy przechowywać w jednej kolekcji np wektor, więc tworzymy dla nich wspólny interface.
Przeglądając ten wektor chcielibyśmy wykonać pewne operacje charakterystyczne dla konkretnego obiektu.
Czy zna ktoś jakieś fajne rozwiązanie tego problemu.

0

Jeżeli da się ograniczyć użycie metod do tych z klasy bazowej (w tym celu mogą, a nawet powinny być wirtualne), to nie trzeba kombinować nic poza zadeklarowaniem ich jako virtual i odpowiednim przedefiniowaniem ich w klasach potomnych. Tak aby odpowiadały swojej roli w zależności od typu obiektu.
Dzięki temu nie trzeba robić żadnych rzutów do typów potomnych.

Natomiast jeżeli nie da się ograniczyć użycia interfejsu do tego z bazy i trzeba użyć interfejsów klas potomnych, to jedynym sensownym sposobem jest sprawdzenie typu i jeżeli jest to możliwe (a nie musi), to rzucenie referencji lub wskaźnika do właściwego typu pochodnego. Robi się to prawidłowo właśnie przez dynamic_cast.

Jeżeli typ bazowy nie istnieje jako klasa konkretna i służy tylko do trzymania interfejsu, to warto zadeklarować ją jako abstract. Podobnie jeżeli metody, które miałyby być polimorficzne nie miałyby mieć sensownej implementacji w klasie bazowej, to wtedy trzeba je zrobić pure virtual. Wtedy każdy obiekt pochodny musiałby je mieć zdefiniowane przed utworzeniem pierwszego obiektu.

0

Nie rozumiem czemu Ci dynamic_cast nie pasuje, skoro służy właśnie do tego celu który chcesz osiągnać.


vector<Base*> objects;

D1* d1=NULL;
D2* d2=NULL;

for (vector<Base*>::iterator i=objects.begin();i=objects.end();i++)
{

    d1 = dynamic_cast<D1*>( *i );
    if(d1)
    {
       ...
    }
    else if(dynamic_cast<D2*>( *i ) )
    {
       ...
    }
}
0

A co sądziecie o takim podejści, żeby to sam obiekt zawołał odpowiednią metodę, która go obsłóży.

class Manager;

class B{
public:
   virtual void processB(Manager * p) = 0;
};

class D1 : public B{
public:
   void processB(Manager * p);
   void someD1() { cout << "D1" << endl; }
};

class D2 : public B{
public:
   void processB(Manager * p);
   void someD2() { cout << "D2" << endl; }
};

class Manager{
public:
   void processD1(D1 * p){
      p->someD1();
      //Inne metody dla D1
   }
   void processD2(D2 * p){
      p->someD2();
      //inne metody dla D2
   }
};

void D1::processB(Manager * p) { 
   p->processD1(this);
}

void D2::processB(Manager * p) {
   p->processD2(this);
}


int main(void){
   B * p = new D2();
   Manager * mn = new Manager();

   p->processB(mn);
   ...
 
0

W ten sposób przesuwasz tylko swój problem z klas Dx do klasy Managera. Powinieneś też zapomnieć o używaniu nazwy klasy w nazwie metody. Jest to zły nawyk bo prowadzi do błędów w których coś inaczej się nazywa niż robi.
Co do koncepcji, to generalnie dziedziczenia unikaj. Jeżeli w każdym istniejącym przypadku możesz powiedzieć, że typ D jest również typem B, to użyj dziedziczenia. W przeciwnym wypadku używaj kompozycji, czyli obiekt jakiegoś typu pakuj do klasy jako pole/referencję prywatną i ewentualnie deleguj część metod.
Nie skupiaj się na "projektowaniu obiektowości" bo to bez sensu. Skup się na rozwiązaniu problemu. "Obiektowość" jest tylko narzędziem, nie celem.

Wracając do tematu - dopiero teraz zauważyłem, że źle sformułowałeś temat. Jeżeli masz wskaźnik do typu bazowego to nigdy nie musisz pozyskiwać instancji obiektu pochodnego bo ty ją już masz - właśnie w postaci wskaźnika do klasy bazowej. Jedyne co możesz pozyskać, to interfejs klasy pochodnej. I od tego jest dynamic_cast.
ps. C++ nie jest do końca językiem obiektowym i pewne rzeczy trochę zaciemnia.

0

Jeżeli chcesz np. do wektora wpakować wszystkie obiekty, a tylko na niektórych zrobić jakieś operacje to powinieneś zrobić klasę abstrakcyjną, wspólną dla wszystkich obiektów. Następnie z niej rób bardziej konkretne klasy, a gdy potrzebujesz coś podziałać na danej klasie to używaj dynamic_cast.

class Object
{
};

class Konkretna : public Object
{
};

// jeśli coś chcesz zrobić tylko dla Konkretna to robisz coś w stylu:
// pseudokod

Konkretna* obiekt = dynamic_cast< Konkretna* >( wektor[ x ] );
if( obiekt )
{
  obiekt->specyficznaMetoda();
}

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