Newsgroup: it.comp.lang.c++
Titolo: dynamic_cast e polimorfismo (visto che siete così disponibili io ne approfitto)

Sotto trovate il codice compilabile.
Problema: ho tre classi, una classe base, e due derivate.
Supponiamo che io voglia utilizzare il polimorfismo tramite il dynamic_cast,
per esempio dichiaro una lista di puntatori alla classe base ma poi mi
accorgo che ho la necessità di accedere a metodi membro di una soltanto
delle classi derivate senza poter modificare il codice della classe base.
Non importa cosa io faccia, ma sempre e comunque, inevitabilmente, il
dynamic_cast mi lancia una eccezione di natura sconosciuta, sia che io casti
un puntatore a base nel suo tipo effettivamente puntato, sia che io cerchi
di castare un puntatore a base in un tipo che in realtà non è il suo.
#include<iostream>
using namespace std;
class bb
{
public:
 virtual int a(){return 0;}
};
class d1: public bb
{
public:
  int a(){return 0;}
};
class d2: public bb
{
public:
 int a(){return 0;}
 int b(){return 0;}
};
int main()
{
  bb* bbp=new bb;
 d1* d1p=new d1;
 d2* d2p=new d2;
 bbp=bbp;
 d2p=0;
 try
 {
  d2p=dynamic_cast<d2*>(bbp);
 }
 catch(bad_cast a)
 {
  cout<<"duh";
 }
 catch(...)
 {
  cout<<"yuk";
 }
 cout<<"dah";
 return 0;
}
Questo codice mi dà SEMPRE l'output yukdah sia che io metta bbp=bbp; sia che
io metta bbp=d1p; sia che io metta bbp=d2p; come quarta riga del main.
Ciò mi lascia perplesso perchè:
1- ho letto sul Lippman che nel casting di puntatori (a differenza del
casting tra riferimenti in cui si usano le eccezioni) dynamic_cast non
lancia eccezioni ma ritorna il puntatore NULL=0
2- perchè ho letto sulla MSDN (discordanti dal Lippman) che dynamic_cast
lancia bad_cast in caso di errori di casting (cosa che però non avviene,
lancia un eccezione ma non è la bad_cast, e la lancia sia quando dovrebbe
lanciare il bad_cast sia quando dovrebbe terminare il casting con successo).
Dove sbaglio?
Inoltre, a livello progettuale, voi preferireste una soluzione che fa uso di
dynamic_cast usati con cautela e attenzione oppure preferireste aggiungere
il metodo b() (quello presente solo in uno dei derivati) anche nella classe
base? Scegliereste questa seconda soluzione anche se concettualmente tale
scelta sia contraria ai tipi di dati trattati?
Un esempio concreto: classe base dipendenti con metodo virtuale get_paga,
tutti i derivati di dipendenti hanno get_paga, eccetto per i ricercatori,
che hanno get_paga ma anche un metodo get_laboratorio, mettereste il metodo
get_laboratorio come virtuale nella classe base dipendenti anche se solo i
dipendenti ricercatori lo usano?
Il Lippman si limita a dire di usare con cautela il dynamic_cast ma non dice
molto di più.
Grazie ciao.

 solo i dipendenti ricercatori lo usano?
Scusa ma... a cosa ti serve "get_laboratorio( ... )"? Cioè, la dicitura 
"laboratorio" per cosa sta? Cosa restituisce questo metodo?
Ciao!
-- 
---
*************** | Have you ever retired a human by mystake?
*** Azathot *** |
*************** | To contact me, ask me gently. Thanks.

 Ciao!
uh beh ipotizziamo che ci siano anche dei dati associati a questo
laboratorio, il nome, l'indirizzo, il settore di ricerca, la
get_laboratorio() li preleva e li stampa (ad esempio) e ci sono altri metodi
per scriverli leggerli ecc ecc. Dati che possono essere definiti a
piacimento nella classe ricercatore o nella classe dipendenti a seconda di
come si vuole gestire la gerarchia ma che sono concettualmente assolutamente
privi di senso se applicati agli altri tipi derivati (amministratore,
insegnante ecc ecc). Io li definirei nel tipo ricercatore, ma resta il
problema della get_laboratorio() (e degli altri metodi che lavorano su
questi dati presenti sono nella classe ricercatore), conviene metterla come
virtuale ANCHE nella classe base e usare poi il meccanismo della risoluzione
dei metodi virtuali oppure definirla solo nella derivata e castare?
Qui dove studio dicono che se si vuole usare il polimorfismo bisogna
NECESSARIAMENTE (il dynamic_cast non lo hanno nemmeno considerato, nè
citato, mi viene il dubbio che non lo conoscano) mettere tutto come virtuale
nella classe base, anche se concettualmente tali funzioni non hanno senso
per la maggior parte dei tipi derivati. E il dynamic_cast cosa lo hanno
messo a fare?
Nota, qui dove sono io l'esempio più comune di polimorfismo è il seguente:
dato un tipo contenitore lo si vuole riempire con dati misti, tutti questi
tipi sono derivati da una classe base. Dopo di che si vuole eseguire delle
operazioni su questi tipi misti, alcune operazioni sono comuni a tutti i
tipi derivati, altre sono presenti solo in alcune di queste classi derivate.

 derivate.
Non è possibile (come è stato detto in altro thread) realizzare un 
contenitore di dati _veramente_ misti. Almeno un tratto in comune vi deve 
essere. D'altronde, violerebbe uno dei principi della derivazione (il 
principio di sostituzione di Liskov).


 principio di sostituzione di Liskov).
Uhmm... e questo cosa sarebbe?

 Uhmm... e questo cosa sarebbe?
Qualsiasi classe derivata per essere veramente derivata deve essere 
completamente intercambiabile con la classe base. Una classe derivata che 
introduca dei metodi in più rispetto alla base viola questo principio.


 Ciao!
Carino perň non mi convince... in questo senso, se io sotituisco una classe
base con una sua derivata che offre gli stessi metodi pubblici piů altri il
programma continua a funzionare senza problema perchč non usa quei metodi in
piů che anche prima non usava perchč non poteva. Perchč una classe con
qualcosa in piů non č considerata "derivata" secondo Mr Liskov (che
certamente ne sa piů di me e vede piů lontano)?
La tua impostazione per la soluzione del mio problema č certamente molto
bella, probabilmente se il problema fosse stato reale io stesso avrei usato
una impostazione simile applicata direttamente ad un buon database e query
in SQL, invece di usare liste polimorfiche. Il punto della questione č che
quello non č un problema reale, era un esercizio da svolgere usando il
polimorfismo  ^_^ ..... senno il prof non č contento.
Io ci provo: ipotizziamo adesso che tutti i professori di prima categoria
abbiano tra i dati l'orario di ricevimento. Tu devi stampare tale orario con
una funzione print_ricev() che accede ai dati necessari e li stampa (usando
il polimorfismo senno mi freghi di nuovo):
a- dichiari tale funzione come virtuale nella classe base, la ridefinisci
nelle classi derivate in cui serve e la richiami per ogni elemento della
lista?
b- casti tutti gli elementi della lista e se il cast ha successo richiami la
funzione, definita nella sola classe in cui č necessaria, usando il
puntatore alla classe.?
Se hai il Lippman, fondamentalmente la domanda si riassume cosě: č meglio la
soluzione a pagina 999 o quella a pagina 1000 del Lippman?
Le soluzioni c, d, e ed f sono ben accette ma solo se usano liste eterogenee
e polimorfismo.
:-)
Ciao.

 (usando il polimorfismo senno mi freghi di nuovo):
Senti, taglia la testa al toro: fai una bella
        virtual void * get( const std::string& _what )
dove lasci che se la sbrighi lui a restituirti quello che vuoi in base a 
"_what". Almeno il principio di Liskov è salvo!


 "_what". Almeno il principio di Liskov è salvo!
Questa soluzione mi piace ed è applicabile agli esercizi maffi che ci fanno
fare.
Dal punto di vista progettuale sono abbastanza soddisfatto, anche se
continuo a non capire perchè il dynamic_cast non mi funziona. Anche se
abbiamo deciso che non serve, però, il dubbio resta.

 abbiamo deciso che non serve, però, il dubbio resta.
Forse non funziona perché il demiurgo dei programmatori è compassionevole. 
:)


contenitore di dati _veramente_ misti.
::boost::any
::boost::many
sbirish

 ::boost::many
Non è standard.


 Sotto trovate il codice compilabile.
Eccoti un esempio funzionante:
#include <iostream>
class Base
{
    public:
        virtual const char *ClassName() { return "Base"; }
};
class Deriv: public Base
{
    public:
        const char *ClassName() { return "Deriv"; }
        const char *Description() { return "I'm a Deriv instance"; }
};
int main()
{
    Base *bp = new Deriv;
    std::cout << bp -> ClassName() << '\n';
    Deriv *dp = dynamic_cast < Deriv * > ( bp );
    if ( !dp )
    {
        std::cout << "Not a valid cast\n";
        return 1;
    }
    std::cout << dp -> Description() << '\n';
    delete bp;
    return 0;
}
EMG
--
EMAG Software Homepage:     
http://www.emagsoftware.it
The EMG's ZX-Spectrum Page: 
http://www.emagsoftware.it/spectrum
The Best of Spectrum Games: 
http://www.emagsoftware.it/tbosg

 }
Non mi va nemmeno questo. Ma ho capito qual'è il problema.
Mi hanno risposto correttamente su comp.lang.c++
Non è un problema di linguaggio ma di compilatore, non avevo attivato le
RTTI.
Attivandole funziona sia il tuo esempio sia il mio precedente listato.

 > ::boost::many
 > Non è standard.
OK, non e' standard hai ragione in questo senso...
Ma e' scritto in C++ standard.
Quindi puoi andare a vedere i sorgenti e vedere come fare.
La domanda era se si poteva fare... se boost ci riesce allora e' possibile!
boost::any - A class whose instances can hold instances of any type that
satisfies ValueType requirements.
Scarica boost, guarda il file boost/any.hpp e vedrai che e' tutto C++
standard e quindi e' possibile reallizare tale cosa in C++ standard e quindi
la tua affermazione e' falsa.
Sbirish ha provato di fartelo capire ma tu hai risposto in modo
superficiale...
E' vero boost non e' standard, hai detto bene,  me e' scritto in C++
standard e se ci riesce lui.... e' possibile farlo anche noi, no?!?
A presto,
Tommy
P.S. posterei tutto il codice ma sono 188 righe e.... puoi andare a
guardartelo da solo.

 satisfies ValueType requirements.
Credo che il punto che voleva dire azathot è che non essendo standard
significa che non esiste "ufficialmente" (cioé "portabile"). Poi leggendo
quello che hai riportato, vedo che è necessario che le istanze del tipo
mantengano i requisiti di essere riconducibili al tipo "ValueType". Quindi
di fatto non può mantenere un QUALSIASI tipo di oggetto...? Per es., i tipi
primitivi soddisfano i requisiti di ValueType?
Ciao.
Z.

>>> contenitore di dati _veramente_ misti.
un banale std::vector<boost::any> ad esempio contiene dati misti.
non so cosa intenda per _veramente_, e' stato vago, e per questo non
gli si puo' dire nulla.... Tuttavia quando ha detto che boost non vale
perche' non e' standard ha detto una cavolata in quanto boost
e' scritto in C++ standard e non conta il fatto che non sia standard.
Poi, con la frase di Azathot sono concorde, se si da un *certo* significato
a quel veramente. Non mi piaceva solo la sua risposta:
boost non e' standard..... (questa non aveva senso, e' stata SOLO detta per
difendersi!).
A presto,
Tommy

 ValueType?
Sure!
Ciao
Gian Michele

 costruttori.
Infatti non ho mai affermato che i contenitori standard possano
rappresentare collezioni eterogenee di dati!
Ciao!
Z.

 Ciao GianMe!
Ciao!

Ok, ma non e' uno scandalo richiedere dei requisiti che giocoforza
possano escludere alcuni tipi :-)
Ciao
Gian Michele


Una classe derivata non potrebbe introdurre metodi in più rispetto alla 
classe base? Un po' restrittivo questo principio di sostituzione di Liskov.


Se ho capito bene hai una classe base dipendente e delle derivate
bidello, segretario, assistente, insegnante, ecc...
Se è così questo mi pare un approccio sbagliato, perché non credo che queste
mansioni siano definitive. 
In questo modo il cambio di attività di un dipendente risulterebbe piuttosto 
problematico, perché non puoi cambiare la classe di un oggetto.
Trovo che sarebbe molto meglio utilizzare un membro della classe
dipendente per indicare il tipo di mansione svolta; in questo modo gestire il
cambio di attività diventa una cosa banale, come lo diventa il gestire
più mansioni svolte contemporaneamente dal dipendente.
Ciao.
-- 
BSD is for UNIX lovers -- linux is for Windows haters.

H.P.L. <u...@vaila.ble> wrote in message slrnbcfk...@aidee.softshark...

In realtà era solo un esempio. Il problema chiave era un altro e riguardava
il polimorfismo e come gestire i metodi membro. Però ho risolto. Grazie
mille anche a te comunque. In effetti come esempio non era un gran che.

 introduca dei metodi in più rispetto alla base viola questo principio.
Non mi pare proprio... Il principio, detto in parole povere, richiede
che la classe derivata possa essere utilizzata al posto della classe
base, ma non il contrario...


 mille anche a te comunque. In effetti come esempio non era un gran che.
OK, non avevo letto tutti i messaggi ed avevo frainteso.
