Newsgroup: it.comp.lang.c++
Titolo: Domanda su ereditarieta'

Salve a tutti,
supponiamo di avere una classe base
class base {...}
e una classe derivata
class derivata : public base {...}
La classe base ha un metodo puramente virtuale met1(). La classe derivata 
definisce met1(), e ha anche un met2() tutto suo.
Se creo un un nuovo oggetto con
base *p = new derivata(...);
posso chiamare p->met1() , ma non p->met2() , corretto?
Dovrei prima castarlo, ad es. static_cast<derivata>(p)->met2() , corretto?
Thx,
Pleg 

 corretto?
usa dynamic_cast
Ciao

 Ciao

 anticipo se il cast si possa fare o meno
...
E' come dici tu:
dynamic_cast è da usare solo quando non sai a priori il vero tipo 
dell'oggetto.
Nel caso dell'OP, e in tutti quei casi in cui hai la certezza sul tipo, 
puoi usare tranquillamente static_cast.
Anzi in questi casi secondo me è generalmente meglio usare static_cast 
che non introduce l'overhead a run time introdotto dal dynamic_cast.
Quindi nel caso specifico secondo me il consiglio di usare dynamic_cast 
è sbagliato.
Beppe

Ottimo grazie.
Ora il vero problema :)
Diciamo che voglio gestire un protocollo a pacchetti. Ci saranno diversi 
tipi di pacchetto (ad es. pktA e pktB che vengono trasmessi, pktC e pktD che 
vengono ricevuti). Mi sembrava una scelta naturale creare una classe 
puramente virtuale "pkt" (da cui far derivare tutte le altre) in cui mettere 
alcuni metodi pur. virt. comuni a tutti i pacchetti, e poi aggiungere dei 
metodi specifici per ogni tipo di pacchetto.
Ad esempio, pktA e pktB avranno un metodo send() , che naturalmente non ha 
senso per pktC e pktD.
Problema: volevo definire un buffer di pacchetti come
vector<pkt*> sendBuffer;
in cui accodare i pacchetti da trasmettere. Ma non posso poi 
chiamare ->send() su questi, perche' non esiste un metodo pur. virt. send() 
nella classe base.
Come risolvere? Mi vengono in mente due vie, ma non sono molto soddisfatto:
1. Definire un metodo send() pur. virt. nella classe base; pktA e pktB lo 
definiscono normalmente, pktC e pktD generano un runtime error se chiamato 
su di esse.
Pro: e' semplice e veloce e non devo cambiare niente nel design del codice 
:)
Contro: preferirei che l'uso errato (e.g. pktC->send() ) venisse rilevato a 
compile time, non a runtime (anche se e' un punto non particolarmente 
critico dal punto di vista pratico, visto che sono l'unico implementatore e 
utilizzatore della classe...)
2. Definire 2 diverse classi base, "pktTx" e "pktRx", con {pktA, pktB} che 
ereditano da pktTx, e {pktC, pktD} che ereditano da pktRx.
Pro: corregge il contro di cui sopra
Contro: se per caso un giorno avessi bisogno di un buffer che contiene tutti 
i tipi di pacchetto misti (A, B, C, D) sono fregato.
Commenti? Suggerimenti? Strategie?
Thx,
Pleg

Pleg ha scritto:

Dato che pktA, pktB, pktC e pktD devono poter essere contenuti nello 
stesso contenitore tipizzato, devono derivare dallo stesso tipo.
Dato che pktA e pktB devono implementare la funzione "send", tale 
funzione deve essere dichiarata da una classe da cui derivano entrambe. 
Dato che pktC e pktD non devono implementare la funzione "send", tale 
funzione non deve essere dichiarata da una classe da cui derivano entrambe.
Detto cio', mi vengono in mente due soluzioni.
La prima e':
class AbstractPkt { ... };
class SendingPkt: public AbstractPkt {
     ... virtual void send() = 0; ...
};
class pktA: public SendingPkt { ... void send() { ... } ... };
class pktB: public SendingPkt { ... void send() { ... } ... };
class pktC: public AbstractPkt { ... };
class pktD: public AbstractPkt { ... };
La seconda e':
class AbstractPkt { ... };
class ISendingPkt { ... virtual void send() = 0; ... };
class pktA: public AbstractPkt, public ISendingPkt {
     ... void send() { ... } ...
};
class pktB: public AbstractPkt, public ISendingPkt {
     ... void send() { ... } ...
};
class pktC: public AbstractPkt { ... };
class pktD: public AbstractPkt { ... };
Concettualmente, la prima soluzione e' indicata se un oggetto SendingPkt 
e' un tipo di AbstractPkt, mentre la seconda e' indicata se un oggetto 
SendingPkt non e' necessariamente un tipo di AbstractPkt, e se la sua 
implementazione non contiene variabili membro, in quanto l'eredita' 
multipla crea problemi se piu' una classe base contiene variabili membro.
-- 
Carlo Milanesi
http://digilander.libero.it/carlmila

 class pktD: public AbstractPkt { ... };
Quindi alla fine posso dichiarare un
vector<AbstractPkt*> buffer;
che contiene puntatori a pacchetti di ogni tipo, ma su cui posso chiamare ad 
es. "buffer[i] -> send()" ?
Pleg 

 
Nel caso specifico magari non è necessario, ma appena aggiungi una
classe derivata secondo me conviene usarlo; se poi sei sicuro che
arrivi sempre il tipo di dato che ti interessa e le prestazioni
sono cruciali cambi con uno static cast.
Ciao
Andrea

Pleg ha scritto:

No. E mi sembrava che non volevi farlo quando ha scritto "preferirei che 
l'uso errato (e.g. pktC->send() ) venisse rilevato a compile time, non a 
runtime".
Puoi farlo solamente su puntatori a SendingPkt o a pktA o a pktB.
Per chiamare "send" su un puntatore a AbstractPkt, devi castarlo a uno 
dei suddetti tre tipi. Se sai che dinamicamente ha proprio tale tipo, 
puoi usare "static_cast", che non costa niente, altrimenti puoi usare 
"dynamic_cast" e controllare il valore ritornato da tale operatore.


 Puoi farlo solamente su puntatori a SendingPkt o a pktA o a pktB.
Assolutamente ragione, mi ero confuso. Thx!
Pleg 


Rispondo a questo post dopo aver letto i reply anche dell'altro ramo.
A mio parere (cioe' per come vedo io un design object oriented) 
send()/recv() non dovrebbero essere metodi del pacchetto stesso bensi' 
del layer di trasporto, probabilmente lo stesso oggetto in cui tieni il 
vettore di pacchetti (vettore che ti consiglio di implementare con una 
std::deque, oggetto + adatto allo scopo), cosi' avrai dei metodi 
dell'oggetto tipo:
bool send(const AbstractPkt &);
AbstractPkt *recv();
Trovo la soluzione piu` pulita anche perche' se si sviluppa un 
protocollo tipicamente lo si fa da 2 lati, e i pacchetti che da un lato 
ricevi dall'altro li devi inviare, con questo approccio puoi mantenere 
le stesse classi per i pacchetti sia sul server che sul client .
Bye,
  Gabry

 std::deque, oggetto + adatto allo scopo)
[cut]
Grazie dei suggerimenti. In effetti prima avevo semplificato il modello per 
poterlo descrivere piu' brevemente. Quello che ho e':
- una deque di pacchetti (son contento, almeno questo l'avevo azzeccato :)
- i send()/receive() sono in effetti degli encode()/decode() : i pacchetti 
hanno hanno come private dei campi come "int length", "int options", 
"vector<int> data", ... al momento della spedizione chiamo "encode()" e il 
pacchetto si "assembla" in un bytestream, che poi viene spedito ad un altro 
oggetto che modella la porta seriale (e che ha quindi i metodi 
open()/close(), send()/receive(), ... )
E' la prima volta che faccio ognuna di queste cose, quindi c'e' una certa 
incertezza e confusione :)
Pleg