Newsgroup: it.comp.lang.c++
Titolo: Polimorfismo

Ciao, temo di non aver capito bene un paio di cose sul polimorfismo.
Considerate questo pezzo di codice, in cui si definiscono la classe A
e la classe derivata B.
A conserva il dato pizza, mentre B sia pizza che donut.
Sotto al listato commento il comportamento del main()
///// cut here
class A
{
public:
  // Constructors and such
	A();
	A(int p);
	~A(){}
	A const &operator=(A const &other);
	A(A const &other);
	int getPizza(){return pizza;}
private:
	int pizza;
};
class B : public A
{
public:
  // Constructors and such
  B();
  B(int p, int d);
  B(int d);
  ~B(){}
  B const &operator=(B const &other);
  B(B const &other);
	int getDonut(){return donut;}
private:
	int donut;
};
// A implementation
A::A():
	pizza(0)
{cout<<"A()"<<endl;}
A::A(int p):
	pizza(p)
{cout<<"A(int)"<<endl;}
A const &A::operator=(A const &other)
{
	// No identity check
	cout<<"=A"<<endl;
	pizza = other.pizza;
	return (* this);
}
A::A(A const &other):
	pizza(other.pizza)
{cout<<"A(A)"<<endl;}
// B implementation
B::B():
	donut(0)
{cout<<"B()"<<endl;}
B::B(int d):
	A(0),
	donut(d)
{cout<<"B(int)"<<endl;}
B::B(int p, int d):
	A(p),
	donut(d)
{cout<<"B(int, int)"<<endl;}
B const &B::operator=(B const &other)
{
	// No identity check
	cout<<"=B"<<endl;
	donut = other.donut;
	return (* this);
}
B::B(B const &other):
	A(other),
	donut(other.donut)
{cout<<"B(B)"<<endl;}
int main()
{
	//prima parte
	B b(3,5);
	B b1;
	cout<<b.getPizza()<<b.getDonut()<<endl;
	b1=b;
	cout<<b1.getPizza()<<b1.getDonut()<<endl;
	cout << endl;
	//seconda parte
	B *bp = new B(3,5);
	B *bp1;
	cout<<bp->getPizza()<<bp->getDonut()<<endl;
	bp1= new B(* bp);
	cout<<bp1->getPizza()<<bp1->getDonut()<<endl;
	delete bp;
	delete bp1;
}
///// cut here
Il risultato è:
///// begin(cout)
A(int)
B(int, int)
A()
B()
35
=B
05
A(int)
B(int, int)
35
A(A)
B(B)
35
///// end(cout)
Nella prima parte del main si ha uno slicing, ovvero l'oggetto
"copiato" (b1) si ritrova con pizza == 0, perché gli resta quello
dell'inizializzazione di default.
Coi puntatori, invece, nessun problema.
Ora io vorrei sapere, è possibile fare in modo che, senza usare i
puntatori come nella seconda parte, l'assegnamento di B "copi" anche i
dati della classe base, che so, invocando in qualche modo
l'assegnamento di A?
Spero di essere stato chiaro, in caso contrario ditemi cos'è che non
si capisce.
Grazie mille!
giulio
Per favore togliete "!RemoveMe!" dall'indirizzo per rispondere via email
Please remove "!RemoveMe!" from the address to email me

 }
Ciao,
Nicola

 Ciao, temo di non aver capito bene un paio di cose sul polimorfismo.
No, e` che l'assegnamento e il polimorfismo non sono davvero
compatibili.
Il grande Meyers, nel suo eccellente testo "More Effective C++",
passa non poco tempo/spazio a cercare di sviluppare un
assegnamento "giusto" per classi polimorfiche, e NON CI
RIESCE.  Leggi il testo, merita, soprattutto nella edizione
su CD, piu` economica e completa (comprende entrambi
i volumi e vari articoli e puntatori in piu`).
E` uno dei vari argomenti per cui Meyers conclude (come
Lakos, per altre ragioni, in "Large Scale C++"; ecc ecc)
sviluppando il principio: *mai ereditare da una classe
concreta*.  Usare l'eredita` solo su classi astratte e` un
po' estremistico (e fara` magari rabbia, a chi il C++ non
lo conosce tanto bene come Meyers, che e` esattamente
quel che si fa in Visual Basic... si eredita l'interfaccia, non
l'implementazione) ma, al netto, ha tanti vantaggi.
Se non altro, risparmia le notti insonni a cercare di
programmare un "giusto" operator=...:-).
Alex

	  A::operator=(other);
[snip]
Funziona! grazie!
Ma è normale fare tutto sto casino ogni volta che c'è un'eredità?
Ciao, giulio


compatibili.
Ah. Ma allora come si spiega che la soluzione fornita da Nicola
Funziona? :-)
Puoi fornire qualche controesempio?
Boh.
grazie, giulio


Un'altra cosa che ho scoperto: in questo semplice esempio, avrebbe
funzionato anche lasciare che il compilatore fornisse automaticamente
l'operator= per la classe derivata, che avrebbe:
1) invocato l'operator= della classe base
2) assegnato membro a membro i dati della classe derivata
Ovviamente, questo non basta in casi un po' più sofisticati (es.
presenza di puntatori), in cui è necessario definire manualmente
l'operator= della classe derivata.
Ciao, giulio


 Puoi fornire qualche controesempio?
Violerei il diritto d'autore di Meyers se ripostassi il suo ottimo
codice.
Comunque, ecco un tipico problema insolubile della
eredita` fra classi concrete, relativo all'assegnamento
e al polimorfismo.
    class B {
    public:
        virtual void operator=(const B&);
        // ecc
    };
    class A: public B {
    public:
        virtual void operator=(const B&);
        // ecc
    };
Per fare override, A deve avere nel suo operator=
la stessa signature di quello della classe padre,
quindi accettare un B (puo` avere ANCHE un
overload che accetta un A, ma se non ha quello
che accetta un B, non ha l'override).
E allora cosa succede quando...:
    void veciafunz(B& laB, const B& quellaltra)
    {
        // tanta roba poi
        if(condizionerara)
            laB=quellaltra;
        // e tanta altra roba
    }
    //
    A unaA;
    A unaltraA;
    B ancheunaB;
    // ...
    veciafunz(unaA, unaltraA);
    veciafunz(unaA, ancheunaB);
Cosa dovrebbe succedere in questi casi...?  Come
fa un oggetto A a farsi assegnare un oggetto B che
A non e` -- cosa succede a quella parte dei suoi
dati addizionale a quella del B, come la mantiene?
In generale, non puo` mantenerla coerente.  E
allora?  Runtime error quando si verifica la condizione
rara?  All'anima del polimorfismo e della sicurezza
compile-time dei tipi...
Il problema e` che A asserisce di essere-un B, ma,
in presenza di assegnamento e se A e B sono classi
concrete (con dati significativi, e A che aggiunge a
B dati significativi), questo non e` vero, poiche` fra
le capacita` di B c'e` quella di farsi assegnare un
generico B, e A non condivide veramente questa
capacita` (e` il vecchio problema della covarianza,
e nell'assegnamento e` ineliminabile in generale,
anche se certo in qualche caso particolare un trucco
si trova e il codice per un po va... salvo rompersi
alla prima revisione che, aggiungendo qualche nuova
feature, spezzi la particolarita` del caso e riporti
all'insolubile caso generale).
Alex

i volumi e vari articoli e puntatori in piu`).
Uhm... appena finito lo Stourstrup mi sa che lo prendo. Però vorrei
chiederti: l'edizione CD comprende *anche* i libri? No, vero?
Altrimenti non costerebbero meno...
A proposito, quanto risparmio, + o -?
No, perché è vero che poter effettuare ricerche di testo su un
documento digitale è una feature impagabile, ma leggere due libri sul
monitor con l'acrobat reader... (o peggio con Word...) insomma,
preferisco vivere! :)
Ciao, grazie ancora


 preferisco vivere! :)
Ti capisco, tant'e` vero che personalmente, quando a un
libro ci tengo davvero, lo compro su carta _oltre_ ad averne,
se posso, una versione elettronica.  Ma e` vero che io
sono uno spendaccione...
Alex