Questo sito utilizza cookies solo per scopi di autenticazione sul sito e nient'altro. Nessuna informazione personale viene tracciata. Leggi l'informativa sui cookies.
Username: Password: oppure
C/C++ - Ereditarietà e downcasting
Forum - C/C++ - Ereditarietà e downcasting

Avatar
MagoAntò (Normal User)
Rookie


Messaggi: 42
Iscritto: 07/02/2009

Segnala al moderatore
Postato alle 15:25
Martedì, 24/04/2012
Ciao a tutti!

Sto realizzando un programma in C++ per l'implementazione di un vocabolario mediante alberi Red Black. Nella traccia viene espressamente richiesto di fare uso dell'ereditarietà.

Ho strutturato il programma in questo modo:

Codice sorgente - presumibilmente C#

  1. class nodo_BST
  2. {
  3. protected:
  4.         string chiave;
  5.         nodo_BST *sx;
  6.         nodo_BST *dx;
  7.         nodo_BST *padre;
  8.  
  9. public:
  10.         nodo_BST() // Costruttore
  11.         {
  12.                 chiave = "";
  13.                 sx = NULL;
  14.                 dx = NULL;
  15.                 padre = NULL;
  16.  
  17.         }
  18.        
  19.         ~nodo_BST() // Distruttore
  20.         {
  21.         }
  22.  
  23.         string get_chiave()
  24.         {
  25.                 return chiave;
  26.         }
  27.        
  28.         void set_chiave(string c)
  29.         {
  30.                 chiave = c;
  31.         }
  32.        
  33.         nodo_BST* get_sx()
  34.         {
  35.                 return sx;
  36.         }
  37.        
  38.         void set_sx(nodo_BST *sinistra)
  39.         {
  40.                 sx = sinistra;
  41.         }
  42.        
  43.         nodo_BST* get_dx()
  44.         {
  45.                 return dx;
  46.         }
  47.        
  48.         void set_dx(nodo_BST *destra)
  49.         {
  50.                 dx = destra;
  51.         }
  52.        
  53.         nodo_BST* get_padre()
  54.         {
  55.                 return padre;
  56.         }
  57.        
  58.         void set_padre(nodo_BST *p)
  59.         {
  60.                 padre = p;
  61.         }
  62. };
  63.  
  64. class nodo_RB : public nodo_BST
  65. {
  66. private:
  67.         colore c;
  68.  
  69. public:
  70.         nodo_RB():nodo_BST() // invoco il costruttore della superclasse
  71.         {
  72.                 c = nero;
  73.         }
  74.        
  75.         ~nodo_RB()
  76.         {
  77.  
  78.         }
  79.  
  80.         colore get_color ()
  81.         {
  82.                 return c;
  83.         }
  84.  
  85.  
  86.         void set_color (colore colore_in)
  87.         {
  88.  
  89.                 c = colore_in;
  90.         }
  91. };



Come potete vedere, la classe nodo_RB è una sottoclasse di nodo_BST. Il problema è il seguente: mettiamo caso che, per esempio, dopo aver creato un oggetto della classe nodo_RB, debba necessariamente invocare il metodo get_sx  per conoscere il figlio sinistro del nodo corrente. Il compilatore mi da errore, dicendo che il metodo della superclasse restituisce un nodo_BST.

Come risolvere il problema?

Ho letto che può essere utilizzato il downcasting (magari quello dinamico che controlla pure se il tipo "castato" è effettivamente diventato quello richiesto) ma mi chiedevo se fosse possibile prendere un'altra strada.

Grazie in anticipo per le risposte! :)

PM Quote
Avatar
lumo (Member)
Expert


Messaggi: 449
Iscritto: 18/04/2010

Segnala al moderatore
Postato alle 17:21
Martedì, 24/04/2012
Testo quotato

Postato originariamente da MagoAntò:

Ciao a tutti!

Sto realizzando un programma in C++ per l'implementazione di un vocabolario mediante alberi Red Black. Nella traccia viene espressamente richiesto di fare uso dell'ereditarietà.

Ho strutturato il programma in questo modo:

Codice sorgente - presumibilmente C#

  1. class nodo_BST
  2. {
  3. protected:
  4.         string chiave;
  5.         nodo_BST *sx;
  6.         nodo_BST *dx;
  7.         nodo_BST *padre;
  8.  
  9. public:
  10.         nodo_BST() // Costruttore
  11.         {
  12.                 chiave = "";
  13.                 sx = NULL;
  14.                 dx = NULL;
  15.                 padre = NULL;
  16.  
  17.         }
  18.        
  19.         ~nodo_BST() // Distruttore
  20.         {
  21.         }
  22.  
  23.         string get_chiave()
  24.         {
  25.                 return chiave;
  26.         }
  27.        
  28.         void set_chiave(string c)
  29.         {
  30.                 chiave = c;
  31.         }
  32.        
  33.         nodo_BST* get_sx()
  34.         {
  35.                 return sx;
  36.         }
  37.        
  38.         void set_sx(nodo_BST *sinistra)
  39.         {
  40.                 sx = sinistra;
  41.         }
  42.        
  43.         nodo_BST* get_dx()
  44.         {
  45.                 return dx;
  46.         }
  47.        
  48.         void set_dx(nodo_BST *destra)
  49.         {
  50.                 dx = destra;
  51.         }
  52.        
  53.         nodo_BST* get_padre()
  54.         {
  55.                 return padre;
  56.         }
  57.        
  58.         void set_padre(nodo_BST *p)
  59.         {
  60.                 padre = p;
  61.         }
  62. };
  63.  
  64. class nodo_RB : public nodo_BST
  65. {
  66. private:
  67.         colore c;
  68.  
  69. public:
  70.         nodo_RB():nodo_BST() // invoco il costruttore della superclasse
  71.         {
  72.                 c = nero;
  73.         }
  74.        
  75.         ~nodo_RB()
  76.         {
  77.  
  78.         }
  79.  
  80.         colore get_color ()
  81.         {
  82.                 return c;
  83.         }
  84.  
  85.  
  86.         void set_color (colore colore_in)
  87.         {
  88.  
  89.                 c = colore_in;
  90.         }
  91. };



Come potete vedere, la classe nodo_RB è una sottoclasse di nodo_BST. Il problema è il seguente: mettiamo caso che, per esempio, dopo aver creato un oggetto della classe nodo_RB, debba necessariamente invocare il metodo get_sx  per conoscere il figlio sinistro del nodo corrente. Il compilatore mi da errore, dicendo che il metodo della superclasse restituisce un nodo_BST.

Come risolvere il problema?

Ho letto che può essere utilizzato il downcasting (magari quello dinamico che controlla pure se il tipo "castato" è effettivamente diventato quello richiesto) ma mi chiedevo se fosse possibile prendere un'altra strada.

Grazie in anticipo per le risposte! :)


Per risolvere questo problema conosco due opzioni:
1) come dici, il casting
2) http://en.wikipedia.org/wiki/Covariant_return_type

La seconda feature è supportata dal c++ e trovi un link ad un esempio in fondo.  

PM Quote
Avatar
MagoAntò (Normal User)
Rookie


Messaggi: 42
Iscritto: 07/02/2009

Segnala al moderatore
Postato alle 20:07
Martedì, 24/04/2012
Grazie per la risposta. :)

Sto provando un'altra strada: in pratica, definire all'interno della classe nodo_RB dei metodi che rimandino alla classe padre. Qualche esempio...

Codice sorgente - presumibilmente C/C++

  1. nodo_BST* nodo_BST::ricerca_termine (nodo_BST *root, string key)
  2. {
  3.  corpo del metodo...
  4. }



...diventa...

Codice sorgente - presumibilmente C/C++

  1. nodo_RB* nodo_RB::ricerca_termine(nodo_RB* root, string key)
  2.     {
  3.         return (nodo_RB*) nodo_BST::ricerca_termine(root, key);
  4.     }



Ho solo un dubbio sui metodi void, come questo:

Codice sorgente - presumibilmente C/C++

  1. void inserisci_nodo(nodo_RB **root, string key);



che diventa

Codice sorgente - presumibilmente C/C++

  1. void nodo_RB :: inserisci_nodo(nodo_RB **root, string key)
  2. {
  3.         nodo_BST :: inserisci_nodo(root, key);
  4. }



Il compilatore continua a riportarmi questo messaggio: 'inserisci_nodo' : cannot convert parameter 1 from 'class nodo_RB ** ' to 'class nodo_BST ** ' Types pointed to are unrelated; conversion requires reinterpret_cast, C-style cast or function-style cast

Come fare?

Ultima modifica effettuata da MagoAntò il 24/04/2012 alle 20:23
PM Quote
Avatar
lumo (Member)
Expert


Messaggi: 449
Iscritto: 18/04/2010

Segnala al moderatore
Postato alle 20:46
Martedì, 24/04/2012
L'altra strada sarebbe appunto al seconda opzione che ho elencato, e cioè i covariant return types.
Però questi non li puoi applicare agli argomenti come hai cercato di fare; ma non è un problema infatti puoi fare:
Codice sorgente - presumibilmente C/C++

  1. nodo_RB* nodo_RB::ricerca_termine(nodo_BST* root, string key)
  2. {
  3.     return (nodo_RB*) nodo_BST::ricerca_termine(root, key);    
  4. }


Quandi usi il metodo e fai un cast da nodo_RB* a nodo_BST* il cast è sempre valido.
Sta solo attento che non venga passato un nodo_BST*, altrimenti avresti qualche inconsistenza. Ma se dai una buona struttura al progetto non dovrebbe succedere.
Al massimo se ti pare di aver fatto un errore usa dynamic_cast<nodo_RB*>( root );
Se root non fosse un nodo_RB* allora il puntatore ottenuto sarebbe nullo(Beware: richiedere RTTI).

PM Quote
Avatar
MagoAntò (Normal User)
Rookie


Messaggi: 42
Iscritto: 07/02/2009

Segnala al moderatore
Postato alle 19:48
Giovedì, 26/04/2012
Non potendo usare i covariant return type per gli argomenti, ho cambiato la firma del metodo in questo modo:

Codice sorgente - presumibilmente Plain Text

  1. nodo_BST* inserisci_nodo(string key)



...che viene invocato da...

Codice sorgente - presumibilmente C/C++

  1. nodo_RB* inserisci_nodo(string key)
  2. {
  3.   return (nodo_RB*) nodo_BST::inserisci_nodo(key);
  4. }



Il nodo RB viene allocato dinamicamente grazie ad una funzione apposita che viene invocata nel corpo di inserisci_nodo. A questo punto, però, mi chiedo: è formalmente corretto che un metodo della superclasse chiami una funzione (non un metodo, attenzione) che alloca dinamicamente un oggetto della sottoclasse? :-|

Ultima modifica effettuata da MagoAntò il 26/04/2012 alle 19:51
PM Quote