Newsgroup: it.comp.lang.c++
Titolo: Ereditare o non ereditare?

Ciao a tutti, ho un problema con un codice e la vaga sensazione che ci sia una
soluzione direttamente sotto il mio naso, solo che non sono in grado di
vederla.
Io ho bisogno di replicare esattamente delle funzioni per due classi diverse,
l'unica cosa che cambierebbe sarebbe il tipo di uno dei parametri, che pero'
verrebbero usati allo stesso modo; in pratica, la classe base dei parametri
passati e' la medesima, e in quelle particolari funzioni si usano solo
proprieta' relative ad essa.
Il problema e' che queste funzioni sono quelle che "tengono traccia" dei miei
oggetti; in pratica li infilano (o li sfilano) in (o da) una lista che
successivamente dovro' richiamare da altre funzioni, queste si' consapevoli
delle nuove proprieta' e che quindi non possono usare necessitano della classe
figlia; percui non credo di poter banalmente dire: "se entrambe le funzioni
usano oggetti che derivano dalla stessa classe, uso la classe base come tipo
del parametro", poiche' l'oggetto usato, e quindi memorizzato, sarebbe del
tipo base e non del tipo derivato.
O posso confidare che sia possibile fare il typecast inverso dall'oggetto base
a quello derivato? Mi pare improbabile, ma nella vita non si sa mai...
Grazie per gli eventuali consigli.
Ciaotutti,
*

Ciao a tutti, ho un problema con un codice e la vaga sensazione che ci sia una
soluzione direttamente sotto il mio naso, solo che non sono in grado di
vederla.
Io ho bisogno di replicare esattamente delle funzioni per due classi diverse,
l'unica cosa che cambierebbe sarebbe il tipo di uno dei parametri, che pero'
verrebbero usati allo stesso modo; in pratica, la classe base dei parametri
passati e' la medesima, e in quelle particolari funzioni si usano solo
proprieta' relative ad essa.
Il problema e' che queste funzioni sono quelle che "tengono traccia" dei miei
oggetti; in pratica li infilano (o li sfilano) in (o da) una lista, che
successivamente dovro' usare in altre funzioni, questa volta consapevoli delle
nuove proprieta', che quindi necessitano delle classi derivate; percui non
credo di poter banalmente dire: "se entrambe le funzioni usano oggetti che
derivano dalla stessa classe, uso la classe base come tipo del parametro",
poiche' l'oggetto usato, e quindi memorizzato, sarebbe del tipo base e non del
tipo derivato.
O c'e' una regola di coi non sono a conoscenza che impone che sia possibile


 pare improbabile, ma nella vita non si sa mai...
E' uno dei problemi più bastardi dell'ereditarietà! ;-)
Non è che te la puoi cavare con funzioni virtuali? Altrimenti esiste il
dynamic_cast, che ti spiegherà qualcun'altro visto che devo andare a
mangiare ;-)
-- 
Andrea Bergia - studente              Linux Registred User #281550
"The one thing we're all waiting for, is peace on Earth and an end to war,
it's a miracle we need, the miracle, the miracle, 
Peace on earth and end to war today,
That time will come one day you'll see when we can all be friends"

 a mangiare ;-)
C'entra qualcosa con i dynamic two? No eh? Lo supponevo :P
Vediamo... Con l'aiuto del tasto magico ho trovato:
Syntax
dynamic_cast < type-id > ( expression )
If type-id is a pointer to an unambiguous accessible direct or indirect base
class of expression, a pointer to the unique subobject of type type-id is the
result. For example:
class B { ... };
class C : public B { ... };
class D : public C { ... };
void f(D* pd)
{
   C* pc = dynamic_cast<C*>(pd);   // ok: C is a direct base class
                           // pc points to C subobject of pd
   B* pb = dynamic_cast<B*>(pd);   // ok: B is an indirect base class
                           // pb points to B subobject of pd
   ...
}
This type of conversion is called an "upcast" because it moves a pointer up a
class hierarchy, from a derived class to a class it is derived from. An upcast
is an implicit conversion.
[...]
If the type of expression is a base class of the type of type-id, a run-time
check is made to see if expression actually points to a complete object of the
type of type-id. If this is true, the result is a pointer to a complete object
of the type of type-id. For example:
class B { ... };
class D : public B { ... };
void f()
{
   B* pb = new D;               // unclear but ok
   B* pb2 = new B;
   D* pd = dynamic_cast<D*>(pb);      // ok: pb actually points to a D
   ...
   D* pd2 = dynamic_cast<D*>(pb2);   //error: pb2 points to a B, not a D
                              // pd2 == NULL
   ...
}
This type of conversion is called a "downcast" because it moves a pointer down
a class hierarchy, from a given class to a class derived from it.
Direi che ci siamo :) Perdo un po' in prestazioni con i controlli a runtime,
ma in questa particolare circostanza non dovrebbe essere un dramma :)
Grazie!
Ciaotutti,
*

 dramma :)
Se č possibile č meglio evitare il ricorso al dynamic_cast<>.
Ora, se ho ben compreso le tue esigenze, potrebbero esserti d'aiuto quelle
quattro cose che ho scritto su questo NG un po' di tempo fa a proposito
del pattern Visitor.
 
http://groups.google.com/groups?selm=1030913795.7800%40tango.comm2000.it
 
Fai anche una ricerca con Google sul pattern Visitor. Troverai molta roba.
Max

 Max
Ups, l'ho visto ora... Grazie mille, quanto ho letto mi pare decisamente
interessante, ci daro' un'occhiata domani :)
A.


Ok, l'ho letto, ma (a meno che non abbia capito una mazza di quano ho letto)
purtroppo non e' quello che cerco: io non ho bisogno di usare metodi diversi
(diverso codice) per classi diverse, ma accesibili nello stesso modo, ho
bisogno di usare lo stesso metodo (identico codice) per una classe madre e una
classe figlia, accessibili in modo diverso - o anche accessibili allo stesso
modo, purche' sia possibile ricavare la classe figlia avendo solo il puntatore
alla classe madre. Ci sono altri metodi di farlo (il piu' semplice, usare un
file incluso con il codice, che pero' alla fine sarebbe codice duplicato), ma
tutto considerato il dynamic_cast<> potrebbe essere la mia migliore
possibilita'...
Se invece fosse vera l'altra ipotesi (e cioe' non avessi capito una mazza di
quanto ho letto), ti sarei grato se volessi illustrarmi meglio il mio errore
:)
Ciaotutti,
*


Mi spieghi con un esempio, anche in pseudo - codice di cosa hai bisogno?
Non capisco perche' il meccanismo del polimorfismo con l'ereditarieta'
pubblica e metodo virtuale nella classe base non ti e' d'aiuto.
Quando e' che hai necessita' di accedere a un oggetto derivato, avendo un
puntatore alla classe base?
In questo caso se usi il dynamic_cast devi essere sicuro che al puntatore
sia stato assegnato l'indirizzo della classe derivata, ma non penso che sia
la soluzione migliore.
Maurus

 ricavare la classe figlia avendo solo il puntatore alla classe madre.
Qui sono io che non capisco. Perchè devi ricavare la classe derivata?
Se sono "accessibili allo stesso modo" (che interpreto come: sono dotati
della stessa interfaccia), non ti serve: ci pensa il polimorfismo.
Se sono "accessibili in modo diverso", allora il codice per accedervi sarà
(ad un certo punto) necessariamente diverso. E qui entrerebbe in gioco il
pattern Visitor.
Ecco, magari potresti abbozzare un esempietto, tanto per capire se stiamo
parlando delle stesse cose.
Max

 stiamo parlando delle stesse cose.
Rispondendo tanto a te quanto a Maurus, posto uno snippet di pseudocodice
perche' in effetti sono stato un po' fumoso.
Diciamo che ho:
class A
{
...
}
class B : public A
{
...
}
class L
{
public:
    AddElement(class A* Element);
}
class M : public L
{
public:
    AddElement(class B* Element);
}
Ora: il codice all'interno dei due metodi AddElement e' assolutamente identico
(piglia oggetto, fai due cosine che richiedono la conoscenza della sola classe
A, infilalo in una lista), l'unica differenza e' il tipo dell'oggetto su cui
operano: quando io accedo alla lista nella classe M, ho bisogno che contenga
un oggetto di classe B.
POTREI fare in modo che la lista di classe L contenga un oggetto di classe B
anziche' uno di classe A, sicuramente risolverei il problema senza faticare,
ma a me *non serve* avere le proprieta' aggiuntive della classe B nei metodi
che usano la lista della classe L, non vedo alcuna ragione di sprecare spazio
initulmente.
Forse mi sfugge qualcosa...
Ciaotutti,
*

 Forse mi sfugge qualcosa...
Secondo me non sprechi spazio facendo cosi'.
La lista di classe L conterrebbe puntatori ad oggetti di classe B invece che
puntatori ad oggetti di classe A per cui lo spazio sarebbe lo stesso.
Nella lista non ci vai mica a mettere l'istanza stessa della classe.
Per cui entrambe le AddElement diventerebbero AddElement(class B* Element);
Ma dopo ci potrebbe essere il bisogno di eventuali chiamate ad AddElement
con il puntatore a un oggetto di classe A o il tuo programma non ne prevede?
Maurus

 classe M, ho bisogno che contenga un oggetto di classe B.
C'č un motivo per cui M č derivata da L? Se L contiene una lista di A e M
deve usare un lista di B, gli oggetti B si ritrovano con una lista di A
che presumibilmente resterą inutilizzata. Io non deriverei M da L. Se M e
L devono essere in quelche contesto "sostituibili", definirei una classe
base astratta e deriverei entrambi da quella. Detto questo (che comunque
non c'entra necessariamente con il problema in questione), potrei
suggerirti di templatizzare la parte che riguarda AddElement:
 template< class T , class Derived  >
 class AddElement_impl
 { protected:
   std::list<T*>   list_; // uso list<> solo a titolo di esempio
   public:
   void AddElement( T* Element )
   {   /*
           "piglia oggetto, fai due cosine";
           se le "due cosine" richiedono l'uso di metodi
           di M o L, puoi accedere all'oggetto "intero" cosģ:
                Derived* dthis = static_cast<Derived*>(this);
       */
       list_.push_back(Element);
   }
 }
 class L : public AddElement_impl<A,L>
 { 
 }
 class M : public AddElement_impl<B,M>
 {
 }
Max


Uhm, si', credo di capire, eccetto... Il metodo a quel punto dove verrebbe
scritto? Il compilatore non lo ricompilerebbe ad ogni inclusione del .h?
Grazie,
A.