Newsgroup: it.comp.lang.c++
Titolo: Come collegare una struttura dati diversa ad ogni istanza di una classe?

Salve, sto cercando di fare come esercitazione un programma di tipo
gestionale usando le classi appena apprese.
In effetti o costruito una classe che ha dei metodi (o funzioni) e proprietą
simili ai comandi DAO del tipo OpenDatabase, AddNew ecc.
Praticamente volevo collegare una struttura 'x' ad ogni istanza di questa
classe in modo da avere una classe che gestisce l'I/O su file 'z'
indipendentemente dalla struttura dati.
Es.: definita la classe con il nome GestIO
struct dati_clienti {
    char Nome[20];
    char Cognome[30];
    char Residenza[40];
};
struct dati_fornitori {
    char NomeSocietą[40];
    char Residenza[40];
};
void main(void)
{
    dati_clienti *sttClienti;
    dati_fornitori *sttFornitori;
    GestIO Clienti, Fornitori;
    // Ora bisogna collegare la struttura dati "dati_clienti" con l'istanza
Clienti e idem per i fornitori;
    Clienti.OpenDatabase("tblClienti", 10 // Nome file che funge da tabella
                                     , "rw",2 // Modalitą di apertura del
file
                                       struct &sttClienti); // Passaggio
della struttura dati affinche l'istanza utilizzi o la struttura o una copia.
    Fornitori.OpenDatabase("tblFornitori,12,"rw",2,struct &sttFornitori);
.......
......
.......
.....
......
....
}
Ritornando a Noi, Inizialmente ho dichiarato all'interno della classe GestIO
un puntatore di qualsiasi tipo (void *pointer_stt) per poi cercare di
assegnare l'indirizzo della struttura dati appropriata in modo che i metodi
della classe potevano inserire i dati della struttura dati nell'apposito
file.
Da qui nascono due problemi:
1) Il compilatore vuole che si faccia il casting al puntatore definito nella
classe alla struttura dati. Ho cercato di scrivere una cosa del genere cioč:
Clienti.pointer_stt = (struct dati_clienti *) sttClienti;
praticamente vorrei assegnare un blocco di memoria a pointer_stt in modo che
quando userņ il metodo Clienti.UpDate, viene inserito, tramite la funzione
fwrite, nel file specifico.
2) Quando scrivo Clienti.pointer_stt = (struct dati_clienti *) sttClienti;
perchč poi ho un'errore da parte del compilatore se scrivo:
pointer_stt->Campo = 3; al puntatore "stt_pointer gl'ho fatto il casting,
gl'ho passato l'indirizzo della struttura dati ho sbagliato qualcosa ?.
Spero che qualcuno mi possa aiutare a risolvere questo problema.
Grazie anticipatamente
doberm...@tin.it

Scusate, ho trovata la soluzione. Comunque rimane un piccolo problema quale:
Come fare per creare una classe che abbia a che fare in modo dinamico con
strutture di dati differenti ?
In effetti il problema, in parole povere è il seguente -> Creo una classe la
quale deve operare su una struttura dati ed inserirla in un file. Ora il
meccanismo, o meglio le funzioni, sono sempre le stesse ma mi trovo con più
strutture dati diverse da memorizzare in file diversi. In effetti volevo
sapere se è possibile ridurre da 'n' Classi, uguali tra loro come funzioni
ma che si riferiscono ad 'n' strutture dati differenti, ad una singola
Classe che a seconda dei parametri passati (pensavo alla struttura dati), fa
riferimento.
Scusandomi con tutti, in quanto forse non riesco ha spegarmi.


 Scusandomi con tutti, in quanto forse non riesco ha spegarmi.
NO - ho capito assai poco :-)  Correggimi se sbaglio:
1) hai una classe "base" e piu' classi derivate che ereditano dalla
   classe base; inoltre vari oggetti, che sono istanze sia della
  classe base che di quelle derivate.
2) hai un container in cui sono contenuti i tuoi oggetti; tutti pero'
   visti come istanze della classe base.
3) vuoi, dato un oggetto del container, capire a che classe appartiene
   realmente per fare qualcosa di diverso classe per classe.
Se ho capito bene, quindi, le tue classi sono definite sul genere di:
class base {
public:
  base() {};
  virtual ~base() {};
  virtual void output() const { cout << "Sono una base\n"; };
};
class a1: public base {
public:
  a1() {};
  ~a1() {};
  void output() const { cout << "Sono una a1\n"; };
};
class a2: public base {
public:
  a2() {};
  ~a2() {};
  void output() const { cout << "Sono una a2\n"; };
};
("output" e' il metodo che fa qualcosa di diverso a seconda della
classe di appartenenza); ed i tuoi oggetti come:
base B; a1 A1; a2 A2;
Se crei una std::list<base>, puoi ficcarci dentro sia B che A1 ed A2;
ma, iterando sulla lista, tutti gli oggetti del container verranno
visti come se fossero della classe base: in effetti *E'* una lista di
basi.  Se usi sugli oggetti puntati dall'iteratore l'operatore typeid
ti ritorna "base", se provi a costruire un puntatore all'oggetto
iterato e fare un dynamic_cast alla classe derivata ottieni 0.
Per me l'unica soluzione semplice e' quella di avere non una lista di
oggetti base, ma una lista di PUNTATORI:
  list<base *> l;
  l.push_back(&B);
  l.push_back(&A1);
  l.push_back(&A2);
  list<base *>::const_iterator it;
  for (it = l.begin();   it != l.end();   it++) {
    const base *pBase = *it;
    const a1 *pa1;
    const a2 *pa2;
    if ((pa1 = dynamic_cast<const a1 *>(pBase)) != 0) {
      pa1->output();
    } else if ((pa2 = dynamic_cast<const a2 *>(pBase)) != 0) {
      pa2->output();
    } else {
      pBase->output();
    }
  }
Il loop stampa:
Sono una base
Sono una a1
Sono una a2
Un'altra soluzione, invece di usare una cascata di dynamic_cast, e'
quella della RTTI: o attraverso typeid, o mettendo all'interno di "base"
un intero che dica quale e' esattamente la classe istanziata, ed usando
uno switch.
HTH (scusa la lunghezza)
-- 
Maurizio Loreti                         
http://www.pd.infn.it/~loreti/mlo.html
Univ. of Padova, Dept. of Physics - Padova, Italy            
lor...@pd.infn.it

   }
Forse mi sfugge qualche punto importante del discorso, ma non basta fare
for (it = l.begin();   it != l.end();   it++)
  (*it)->output();
EMG

   (*it)->output();
E' vero...


   (*it)->output();
Solo se il metodo "output" e` virtuale (e la dominanza funziona
come richiesto...!).  Se non lo e` (purche` le varie classi in
gioco siano comunque polimorfiche per altre ragioni), il frammento
di Maurizio produce invece risultati diversi dal tuo.
Ad esempio:
class base {
public:
    virtual void output() { std::cout<<"base\n"; }
};
class a1 {
public:
    virtual void output() { std::cout<<"a1\n"; }
};
class basea1: public base, public a1 {
};
Qui la virtualita` c'e`, ma non la dominanza.  Quindi...:
    std::list<base*> ldb;
    ldb.push_back(new base);
    ldb.push_back(new basea1);
Ora il secondo e il primo modo produrranno diversi output.
Per chiarire meglio, ecco un completo esempio giocattolo:
#include <list>
#include <iostream>
class base {
public:
    virtual void output() const { std::cout<<"base\n"; }
};
class a1 {
public:
    virtual void output() const { std::cout<<"a1\n"; }
};
class basea1: public base, public a1 {
};
class a2 {
public:
    virtual void output() const { std::cout<<"a2\n"; }
};
class basea2: public base, public a2 {
};
class basea1a2: public base, public a1, public a2 {
};
void primomodo(const std::list<base*>& l)
{
    std::list<base *>::const_iterator it;
    for (it = l.begin();   it != l.end();   it++) {
        const base *pBase = *it;
        const a1 *pa1;
        const a2 *pa2;
        if ((pa1 = dynamic_cast<const a1 *>(pBase)) != 0) {
            pa1->output();
        } else if ((pa2 = dynamic_cast<const a2 *>(pBase)) != 0) {
            pa2->output();
        } else {
            pBase->output();
        }
    }
}
void secondomodo(const std::list<base*>& l)
{
    std::list<base *>::const_iterator it;
    for (it = l.begin();   it != l.end();   it++)
        (*it)->output();
}
int main()
{
    std::list<base*> ldb;
    ldb.push_back(new base);
    ldb.push_back(new basea1);
    ldb.push_back(new basea2);
    ldb.push_back(new basea1a2);
    ldb.push_back(new basea2);
    std::cout << "Primo modo...:\n";
    primomodo(ldb);
    std::cout << "\n";
    std::cout << "Secondo modo...:\n";
    secondomodo(ldb);
    std::cout << "\n";
    return 0;
}
L'output sara`:
Primo modo...:
base
a1
a2
a1
a2
Secondo modo...:
base
base
base
base
base
Alex

 di Maurizio produce invece risultati diversi dal tuo.
Chiaro. Lo davo per scontato ma hai perfettamente ragione e hai fatto
benissimo a puntualizzarlo.
EMG