PDA

Visualizza versione completa : [C++] Lista di struct


leuconoe
02-09-2009, 15.02.06
Salve gente :)
sto lavorando ad un progetto con linguaggio c++.
Mi è stato chiesto di implementare un template lista basato su un template elemento di lista utilizzando 2 campi generici e i vari puntatori che servono per implementare le liste.
Poi dovrei utilizzare il template lista per costruire una biblioteca.
in questa biblioteca si utlizza la struct Infovolume con vari campi che danno informazioni sul volume.
Quindi credo che debba implementare una lista di struct Infovolume per costruire la classe biblioteca.
Ecco la domanda è:
è possibile usare le funzioni base della lista oppure devo ricostruire ogni funzione per la nuova classe biblioteca?
Cioè ad esempio: per inserire il nome del volume, devo richiamare la funzione inserisci del template lista oppure costruirne un'altra nella classe biblioteca? e se si, che tipo di sintassi bisogna usare?

Scusate le domande ma sul manuale ho semplici esempi di classi che utilizzano come tipo di dato nel template un dato semplice come intero oppure striung. Non so come comportarmi con liste di struct.
Grazie mille


//dichiarazione del template elemento di lista
template<class gen> class List_element {
public:
gen data;
gen key;
List_element<gen> *succ;
List_element<gen> *prec;

List_element();
~List_element();
void set_element(gen a,gen b);
gen get_element();
List_element *get_next();
void set_next(List_element *p);
List_element *get_prev();
void set_prev(List_element *s);

//dichiarazione del template lista bidirezionale
template <class gen> class lista_concatenata_bidirezionale {

public: List_element<gen>* TestaDiLista;
List_element<gen>* CodaDiLista;
int num_elementi;


lista_concatenata_bidirezionale();
~lista_concatenata_bidirezionale();
void Crea_lista();
bool Lista_vuota();
List_element<gen>* ultimo_lista();
gen leggi(List_element<gen>*p);
void scrivi(List_element<gen>*p, gen a,gen b);
void cancella(List_element<gen>*p);
void inserisci(List_element<gen>*p, gen a,gen b);
int getNumElementi();
List_element<gen>* cerca(gen a);
void Stampa_lista();
List_element<gen>* succ_lista(List_element<gen>* pos);
};

//dichiarazione di Infovolume
struct Infovolume
{
string nome;
string disposizione;
int disponibilita';
}

//dichiarazione di Biblioteca
class Biblioteca
{
lista_concatenata_bidirezionale<Infovolume> biblioteca;

.................................................. .....
.................................................. ..........
.................................................. ........
};

P8257 WebMaster
02-09-2009, 16.53.12
Se la lista è specializzata (ovvero non opera con tipi nativi) e devi reimplementare il suo template occorre specializzare anche i metodi che consento di : inserire (push_back) cancellare (remove, removeat opzionale) e iterare (iteratore) poiché operino con l'oggetto struttura o con l'operatore di cui hai bisogno

guarda qui per l'implementazione standard di una lista
http://www.ziggyware.com/readarticle.php?article_id=20

prendi spunto da questo esempio e reimplementa il tuo template poiché sia in grado di operare con la struct di cui hai bisogno, reimplementa soltanto i metodi che ti servono dopodiché usala come una normale lista


listtemplate <InfoVolume> myList ...


utilizzando i metodi implementati per gestirla.

leuconoe
03-09-2009, 19.24.38
Ciao WebMaster e grazie per il consiglio. In realtà ho già implementato sia il template lista che il template elemento di lista come nell'esempio.
Il problema è che non riesco ad operare sui singoli campi della struct utilizzando le funzioni base che operano su tipi generici implementate nei template.
cioè come faccio ad aggiornare ad esempio la quantità di un libro con le sole operazioni definite nel template?

LoryOne
03-09-2009, 23.24.11
Codice semplice per i semplici concetti fondamentali...
Funzioni generiche


/* swapargs è una funzione generica alla quale vengono passati
due parametri di tipo X. X non ha nulla a che vedere con la classe.
Ad X verrà assegnata la tipologia del dato nel momento in cui verrà
invocata la funzione swapargs.
*/
template <class X> void swapargs(X &a, X &b){
/* A swapargs vengono passati gli indirizzi di a e b il cui valore dipenderà
dal tipo del dato specificato al momento della compilazione del codice.
*/
X temp; // temp è una variabile temporanea di tipo X.
// Il tipo di dato referente ad a e b sarà specificato
// al momento della chiamata alla funzione swapargs.

temp = a;
a = b;
b = temp;
cout << "\nvalore di parametro1:" << a
<< " valore di prametro2:" << b << "\n";
};

int main(void){
// swapargs è sempre la stessa funzione, ma il tipo del parametro cambia.
int i=10;
int j=20;
cout << "valore di parametro1:" << i
<< " valore di parametro2:" << j;
swapargs(i,j);

char c='a';
char d='b';
cout << "valore di parametro1:" << c
<< " valore di parametro2:" << d;
swapargs(c,d);

return 0;
}


Classi generiche


/* MyClass è una classe generica alla quale vengono passati
due parametri di tipo X. X non ha nulla a che vedere con la classe.
Ad X verrà assegnata la tipologia del dato nel momento in cui verrà
creato il costruttore di MyClass.
*/
template <class X> class MyClass{
X x,y; //x ed y sono due variabili private generiche di tipo X
public:
// Costruttore della classe MyClass
MyClass(X a, X b){ // a e b sono due variabili generiche di tipo X
x = a;
y = b;
}
// div() è una funzione generica della classe MyClass di tipo X
X div(){ return x/y; }
};

int main(void){
/* d_ob è una classe generica che eredita da MyClass membri e funzioni il
cui tipo è specificato all atto del creazione del costruttore della classe
*/
MyClass<double> d_ob(10.0, 3.0);
cout << "divisione di double: " << d_ob.div();

/* i_ob è una classe generica che eredita da MyClass membri e funzioni il
cui tipo è specificato all atto del creazione del costruttore della classe
*/
MyClass<int> i_ob(10, 3);
cout << "\ndivisione di interi: " << i_ob.div();
return 0;
}


Classi specializzate


/* MyClass è una classe generica alla quale vengono passati
due parametri di tipo X. X non ha nulla a che vedere con la classe.
Ad X verrà assegnata la tipologia del dato nel momento in cui verrà
creato il costruttore di MyClass.
*/
template <class X> class MyClass{
X x,y; //x ed y sono due variabili private generiche di tipo X
public:
// Costruttore della classe generica MyClass
MyClass(X a, X b){ // a e b sono due variabili generiche di tipo X
x = a;
y = b;
}
// div() è una funzione generica della classe MyClass di tipo X
X div(){ return x/y; }
};

// I parametri passati a MyClass vengono definiti in modo specifico,
template <> class MyClass<int>{
int x, y; // x ed y sono due variabili private all'interno della classe
public:
// Costruttore della classe specifica MyClass
MyClass(int a,int b){
x = a;
y = b;
}
// div() è una funzione della classe MyClass di tipo intero.
int div(){ return x/y; }
};

int main(void){
/* d_ob è una classe generica che eredita da MyClass membri e funzioni il
cui tipo è specificato all atto del creazione del costruttore della classe
*/
MyClass<double> d_ob(10.0, 3.0);
cout << "divisione di double: " << d_ob.div();

/* i_ob è una classe generica che eredita da MyClass membri e funzioni il
cui tipo è specificato all atto del creazione del costruttore della classe
*/
MyClass<int> i_ob(10, 3);
cout << "\ndivisione di interi: " << i_ob.div();
return 0;
}

LoryOne
04-09-2009, 00.12.21
Ok, ora abbiamo gettato le basi per cominciare a comprendere la classe List http://www.cplusplus.com/reference/stl/list/

P8257 WebMaster
04-09-2009, 10.24.40
Non sono sicuro di aver capito bene il problema dell'operare coi campi, forse perché stai cercando di operare con una lista despecializzata su un contesto specializzato...

Oltre ai link e agli spunti che ti ha fornito Lory volevo suggerirti di guardare nuovamente il link dell'implementazione standard ed in particolare...


void push_back(T Item)
{
bValid=false;

if(m_pEnd)
{
m_pEnd->m_pNext = new ListNode(Item);
m_pEnd->m_pNext->m_pPrev = m_pEnd;
m_pEnd = m_pEnd->m_pNext;
}
else
{
m_pHead = new ListNode(Item);
m_pEnd = m_pHead;
}
m_dwSize++;
}

void Insert(T Item,ListNode* pPrev = 0)
{
bValid=false;
m_dwSize++;

if(!pPrev)
{
ListNode* pBase = m_pHead;
m_pHead = new ListNode(Item);
m_pHead->m_pNext = pBase;
if(!pBase)
pBase = m_pHead;
pBase->m_pPrev = m_pHead;
if(!m_pEnd)
{
m_pEnd = pBase;
}
}
else
{
ListNode* pNext = pPrev->m_pNext;
if(!pNext)
{
pPrev->m_pNext = new ListNode(Item);
pPrev->m_pNext->m_pPrev = pPrev;
if(m_pEnd == pPrev)
{
m_pEnd = pPrev->m_pNext;
}
}
else
{
pPrev->m_pNext = new ListNode(Item);
pPrev->m_pNext->m_pPrev = pPrev;
pPrev->m_pNext->m_pNext = pNext;
pNext->m_pPrev = pPrev->m_pNext;
if(m_pEnd == pPrev)
{
m_pEnd = pPrev->m_pNext;
}
}
}
}



Come vedi, se la tua lista, al posto di essere generica fosse specifica (come in realtà dovrebbe essere) il problema dell'operare coi singoli campi non ce l'hai perché operi sul puntatore all'elemento listNode che nel tuo caso è l'intera struttura InfoVolume, ne consegue che per "aggiornare" la quantità di un certo volume in lista, a meno che non vuoi implementarti un metodo Update (ma il gioco non vale la candela) devi estrarre l'item dalla lista, crearne un'istanza per referenza, modificare il valore dei capmi di struttura, rimuoverlo dalla lista e riaggiungerlo, a tale scopo usa il metodo RemoveAt e Push_back...

LoryOne
06-09-2009, 12.10.55
Abbiamo visto che l'operatore new viene utilizzato per allocare ed eventualmente popolare un array di qualsiasi tipo attraverso l'utilizzo di un puntatore alla memoria dinamica (heap).
La classe generica list, utilizzabile attraverso l'inclusione dell'header <list.h> (#include <list>), fa ampio uso di template e di strutture che puntano a loro stesse, in modo tale da creare un array espandibile (dinamico) di oggetti.
Il link da me proposto, mostra come utilizzare la classe list in modo da creare un array di interi, ma non è molto chiaro su come gestire un array di strutture.
Una struttura è anch'essa un tipo di dato, così come lo sono int e char (persino una classe), ma può essere costituita da differenti tipi di dato, nonchè funzioni inline.

Facciamo un esempio semplice e chiaro:

1 - Creiamo la struttura MiaStruct1
struct MiaStruct1{
char x;
int y;
};

2 - Creiamo ora una classe list di tipo MiaStruct1
list<MiaStruct1> L1;

3 - Identifichiamo un puntatore alla struttura MiaStruct1
MiaStruct1 *p;

4 - Utilizziamo la classe L1 per allocare un array dinamico di 3 elementi di tipo MiaStruct1
p=L1.get_allocator().allocate(3);
p ora punta ad una lista concatenata di tre elementi di struttura MiaStruct1

5 - Popoliamo ogni elemento dell'array.
p[0].x='A' ; p[0].y=1 ;
p[1].x='B' ; p[1].y=2 ;
p[2].x='C' ; p[2].y=3 ;

6 - Visualizziamo ogni elemento dell'array
for(int i=0; i<3; i++) cout << p[i].x << p[i].y << endl;

7 - Infine deallochiamo la memoria dinamica allocata in precedenza.
L1.get_allocator().deallocate(p,3);

Vediamo ora un ulteriore esempio di utilizzo della classe facendo riferimento ai membri pubblici esplicitamente predisposti, al fine di ottenere un approccio più C++ alla classe list.

Facciamo un esempio semplice e chiaro:
1 - Creiamo la struttura MiaStruct2
struct MiaStruct2{
char x_;
int y_;
MiaStruct2(char x, int y) : x_(x), y_(y) { };
};
In questo caso, MiaStruct2 diventa una sorta di costruttore inline di se stessa che contiene al suo interno il prototipo.
si potrà popolare MiaStruct2 chiamandola direttamente ed assegnando ad x_ il valore di x ed ad y_ il valore di y.

2 - Creiamo ora una classe list di tipo MiaStruct2
list<MiaStruct2> L2;

4 - Utilizziamo la classe L2 per allocare un array dinamico di 3 elementi di tipo MiaStruct2
L2.push_back(MiaStruct2('a',1));
L2.push_back(MiaStruct2('b',2));
L2.push_back(MiaStruct2('c',3));

5 - Ora è necessario implementare un puntatore ad ogni elemento dell'array, in modo da poter iterare su ogni elemento di cui si compone l'insieme.
list<MiaStruct2>::iterator it2;

6 - Visualizziamo ogni elemento dell'array
for(it2=L2.begin(); it2!=L2.end(); it2++) cout << (*it2).x_ << (*it2).y_ << endl;

In ultimo, il programma completo:


struct MiaStruct1{
char x;
int y;
};

struct MiaStruct2{
char x_;
int y_;
MiaStruct2(char x, int y) : x_(x), y_(y) { };
};

main(void){

list<MiaStruct1> L1;
MiaStruct1 *p;

p=L1.get_allocator().allocate(3);
p[0].x='A' ; p[0].y=1 ;
p[1].x='B' ; p[1].y=2 ;
p[2].x='C' ; p[2].y=3 ;

for(int i=0; i<3; i++)
cout << p[i].x << p[i].y << endl;
L1.get_allocator().deallocate(p,3);

list<MiaStruct2> L2;

L2.push_back(MiaStruct2('a',1));
L2.push_back(MiaStruct2('b',2));
L2.push_back(MiaStruct2('c',3));

list<MiaStruct2>::iterator it2;
for(it2=L2.begin(); it2!=L2.end(); it2++)
cout << (*it2).x_ << (*it2).y_ << endl;

}


Ps: ringrazio il web per aver postato un ottimo esempio di implementazione di una classe generica che faccia uso di liste concatenate per ottenere un vettore dinamico di oggetti.

P8257 WebMaster
07-09-2009, 10.17.02
(Y)...