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

ho un vuoto di memoria..
se ho questa gerarchia
class nonna {
public:
	nonna(int);
};
class mamma : public nonna {
public:
	mamma(un altro int);
};
classe figlia : public mamma {
public:
	figlia(ancora un int );
};
COME POSSO, dal costruttore di figlia, chiamare direttamente il 
costruttore di nonna???
ho provato, a naso:
figlia::figlia(int a ) : mamma::nonna(a) {}
e
figlia::figlia(int a ) : nonna(a) {}
ma entrambi non funzionano....
qualcuno ha una risposta pronta???
grazie,
andrea

 
Non vorrei prendere una bufala gigante, ma mi pare che non si possa.
nell'initializer list di una classe "figlia", si puo' solo chiamare:
- il costruttore della classe base "madre" (non "nonna")
- i costruttori di variabili membro della classe "figlia"
E' responsabilita' di ogni classe derivata inizializzare la classe da 
cui deriva direttamente.
-- 
Roal Zanazzi

 
A meno che la classe base non sia virtuale; in tal caso *devi* chiamare 
esplicitamente il suo costruttore.
  bye
	av.

 costruttore di nonna???
In generale non si puo'. Quello che puoi pero' fare e' sfruttare il
fatto che l'eredita' virtuale impone il vincolo che il costruttore
della classe base virtuale sia chiamato da quello della classe piu'
derivata (e di conseguenza da tutte quelle intermedie). Ad esempio:
struct A {
  A(int i) { std::cout << "A(" << i << ")\n"; }
};
struct B : virtual A {
// La chiamata al costruttore di A e' obbligatoria, ma non verra'
effettuata perche'
// B non e' la classe piu' derivata.
  B() : A(9) { std::cout << "B()\n"; }
};
struct C : B {
  C(int i) : A(i) { std::cout << "C(" << i << ")\n"; }
};
int main()
{
  C c(7);
}
Ciao,
Nicola

Andrea Venturoli ha scritto:

Gia', mi pareva di aver dimenticato qualcosa :-P
-- 
Roal Zanazzi

 derivata (e di conseguenza da tutte quelle intermedie). Ad esempio:
okkei, grazie a tutti per aver risposto.
gia` che siamo entrati in tema di erediterieta` virtuale, ne approfitto 
per farvi un'altra domanda. io, sebbene programmi in c++ da diversi 
anni, confesso che non ho mai sentito la necessita` di ereditare in 
maniera 'virtuale' (ovvero come nel tuo esempio: struct B : virtual A {} 
), perche` l'ereditarieta` normale mi e` sempre stata (piu` che) 
sufficiente.
qualcuno mi sa fare un esempio pratico in cui l'ereditarieta` virtuale 
e` *veramente* necessaria o anche solo utile?
dalle mie letture ricordo che era pensata per i casi in cui si ha una 
gerarchia di qesto tipo:
          classe A
        /          \
       /            \
classe B1 : A      classe B2 : A
      \             /
       \           /
	classe C : B1, B2
e si vuole evitare di 'replicare' l'istanza di A, in C, facendo si` che 
C::B1 e C::B2 condividano la stessa istanza di A come base. dico bene??
pero`, come vi dicevo.. no ho mai sentito la necessita` di fare una cosa 
del genere. magari e` pensata per qualche casistica specifica nella 
quale non son mai incappato?? o la mia immaginazione, non avendone 
afferrato la profonda utilita`, non e` stata capace di farne buon uso??
qualcuno ha esperienze diverse??
ciao, ciao. grazie.
andrea

 afferrato la profonda utilita`, non e` stata capace di farne buon uso??
Io la uso regolarmente per combinare interfacce. Provo a delinearti un
esempio, che e' una semplificazione di quello che faccio. In ambito
finanziario hai questi concetti:
Posizione - lo stato di un investimento o insieme di investimenti
Contratto - Sottoscrizione di una porzione di un prodotto da parte di
un cliente
Portafoglio - Insieme di contratti
Sia Contratto che Portafoglio sono Posizioni; in effetti implementano
il pattern Composite, in quanto Portafoglio e' una Posizione in quanto
aggregazione di contratti. Ad es. (alcune classi non hanno membri,
ricorda che sono tutte interfacce):
class Posizione {
  public:
    virtual double valore() = 0;
};
class Contratto : public Posizione {
};
class Portafoglio : public Posizione {
};
Supponi pero' che oltre a questa rappresentazione generica, io abbia
bisogno di distinguere le informazioni relative poniamo ai Fondi da
quelle relative alle Sicav (due tipi di prodotti finanziari).
Mi serviranno le classi PosizioneFondi, ContrattoFondi,
PortafoglioFondi, piu' le corrispondenti per le Sicav. L'eredita'
virtuale mi serve perche' Un ContrattoFondi e' sia una PosizioneFondi
che un Contratto, ma e' un'unica Posizione, non due. Avro' quindi
class PosizioneFondi : public virtual Posizione {};
// modificata rispetto a sopra
class Contratto : public virtual Posizione {};
class ContrattoFondi : public PosizioneFondi, public Contratto {};
Analogamente per PortafoglioFondi e per le classi relative alle Sicav.
Questo mi permette in contesti diversi di considerare come uniformi
contratti relativi a fondi e sicav, ma anche contratti e portafogli.
Ciao,
Nicola

 qualcuno ha esperienze diverse??
Forse non hai capito esattamente cosa succede. Considera il seguente 
esempio:
---
#include <iostream>
using namespace std;
struct Base
{
	int x;
};
struct A : virtual Base
{
	void printa() { cout << "A::x = " << x << endl; }
	void seta(int a) { x = a; }
};
struct B : virtual Base
{
	void printb() { cout << "B::x = " << x << endl; }
	void setb(int b) { x = b; }
};
struct C : A, B
{
};
int main()
{
	C c;
	c.seta(5);
	c.setb(10);
	c.printa();
	c.printb();
}
---
Considera un oggetto O e due interfacce I1 e I2.
Puoi avere queste 3 combinazioni:
O + I1
O + I2
I1 + O + I2
Nel terzo caso, puoi modificare lo stesso O tramite I1 ed I2. La 
modifica eseguita tramite I1 viene vista anche da I2 e viceversa.
L'utente passa da I1 ad I2 per un fatto di pura comodità. Se O non fosse 
lo stesso, sarebbe costretto a fare o tutto con I1 o tutto con I2.
Se hai due classi O1 e O2 t.c.
O1 := O|I1
O2 := O|I2
e ora crei una classe
C := O1|O2
puoi ottenere due situazioni diverse:
1) C = O|I1|O|I2
2) C = O|I1|I2
Se quello che desideri è manipolare lo stesso O tramite le due 
interfacce, allora devi usare "virtual" in modo da trovarti nella 
situazione 2.
Piuttosto che pensare che i due "O" si fondano, conviene pensare che uno 
dei due viene buttato via ;)
Kiuhnm

 
Ciao Nicola,
ne approfitto della tua esperienza.
Supponiamo di avere un progetto in cui si fa uso di interfacce pure 
(senza membri dati e con solo metodi virtuali = 0)
In pratica avrò una gerarchia di interfacce e a priori potrei non sapere 
che uso ne intende fare chi implementerà le interfacce. (dopotutto le 
interfacce servono proprio a questo: disaccoppiare concetti astratti e 
concetti concreti...)
Allora secondo te è corretto applicare *sempre* la derivazione virtuale 
alle gerarchie di interfacce pure?
In questo modo se chi implementa classi "concrete" deve abbinare più 
interfacce, non avrà problemi di "diamante".
Ci sono controindicazioni ad usare questa regola?
Addirittura anche quando si implemetano interfacce pure si potrebbe 
usare *sempre* la derivazione virtuale.
In questo modo mi sembra che si possano simulare i meccanismi di 
ereditarietà "semplificata" alla C#:
al massimo una sola derivazione di implementazione,
tutte le derivazioni di interfaccia che si vuole.
E' corretto?
Es.
class IBase
{
public:
virtual int TestBase() = 0;
};
class IDer1 : virtual public IBase
{
public:
virtual int TestDer1() = 0;
};
class IDer2 : virtual public IBase
{
public:
virtual int TestDer2() = 0;
};
...
class CBase
{
public:
int a;
};
//la derivazione da IBase è inutile, ma l'implementatore potrebbe non
//saperlo o decidere di esplicitarla per maggior chiarezza.
class CConcreta : public CBase, virtual public IBase, IDer1, IDer2
{
public:
virtual int TestBase() {return 0;}
virtual int TestDer1() {return 1;}
virtual int TestDer2() {return 2;}
};

 tutte le derivazioni di interfaccia che si vuole.
Non sono convinto che questo sia un principio cosi' importante, perche'
rende estremanente difficile il riutilizzo delle implementazioni. In
questo per contro sono particolarmente utili i template. Io uso
svariate classi template della forma
template <typename InterfacciaT>
class Implementazione : public InterfacciaT {
};
ciascuna delle quali realizza una variante dell'implementazione di una
certa interfaccia. In questo modo posso combinarle a piacere.
Ciao,
Nicola

 
Anche io sono d'accordo che il meccanismo "alla C#" penalizza il riuso, 
anche se certi puristi direbbero che il riuso per ereditarietà (o riuso 
white box) è male, mentre il riuso per composizione (black box) è bene.
In un certo senso, l'approccio "alla c#" ti costringe a favorire il 
black box, anche se è noioso usare la delega per implementare le varie 
interfacce...
Anche se credo che i progettisti di C# (e gli altri linguaggi di moda) 
abbiano imposto questo vincolo soprattutto per schivare rogne di 
implementazione dei compilatori.
Sui template sono d'accordo.
Ciao
BB