Newsgroup: it.comp.lang.c++
Titolo: Liste C++

Sono ancora io
Dopo le risposte e i suggerimenti avute ad un post precente, ho provato ad
implementare una lista doppiamente linkata, soprattutto per fare un po' di
pratica e di esercizi.
Però non va
all'interno della funzione      inserisci    , non riesco ad accedere in
nessun modo agli elementi dell'oggetto che gli passo come parametro.
-------------- CODICE ------------------
#include <conio.h>
#include <iostream.h>
class ListElement;
typedef ListElement* ListElementPtr;
class ListElement {
private:
   int value;
    ListElementPtr next;
    ListElementPtr previous;
public:
 ListElement();
 void WriteValue(int newvalue);
    void WriteNext(ListElementPtr newnext);
    void WritePrevious(ListElementPtr newprevious);
    int ReadValue(void);
    ListElementPtr ReadNext(void);
    ListElementPtr ReadPrevious(void);
};
// Funzioni membro
// Costruttore
ListElement :: ListElement() {
 next = previous = NULL;
}
int ListElement :: ReadValue() {
    return value;
}
ListElementPtr ListElement :: ReadNext() {
    return next;
}
ListElementPtr ListElement :: ReadPrevious() {
    return previous;
}
void ListElement :: WriteValue(int newvalue) {
    value = newvalue;
}
void ListElement :: WriteNext(ListElementPtr newnext) {
    next = newnext;
}
void ListElement :: WritePrevious(ListElementPtr newprevious) {
    previous = newprevious;
}
// Funzione inserimento
ListElementPtr insert(int e, ListElementPtr l) {
 ListElementPtr t;
 t = new ListElement;
    t->WriteValue(e);
 t->WriteNext(l);
 l->WritePrevious(t);            //  qui si blocca
                                           //  sia con questa che qualsiasi
altra funzione applicata a l
    return t;
}
void main(void) {
 ListElementPtr root = NULL;
    ListElementPtr l;
    int i;
    do {
     cout << "Introduzione valore: ";
     cin >> i;
     root = insert(i, root);
    }while(i != 0);
    // stampa
    l = root;
    while(l != NULL) {
     cout << "Valore estratto:" << l->ReadValue() << endl;
       l = l->ReadNext();
   	}
   	getch();
}

     root = insert(i, root);
La chiami con un secondo argomento appena settato
a NULL, e quindi, _ovviamente_, non puoi chiamarci
su nulla...!
Metti nella tua insert, almeno, i necessari test del
tipo "if(l)"...
Alex


Hai, cominci a giocare con i puntatori, e non sai ancora usare il debugger !
#include <conio.h>
#include <iostream.h>
class ListElement;
typedef ListElement* ListElementPtr;
class ListElement {
private:
   int value;
    ListElementPtr next;
    ListElementPtr previous;
public:
ListElement();
void WriteValue(int newvalue);
    void WriteNext(ListElementPtr newnext);
    void WritePrevious(ListElementPtr newprevious);
    int ReadValue(void);
    ListElementPtr ReadNext(void);
    ListElementPtr ReadPrevious(void);
};
// Funzioni membro
/******************************/
// Riscritto il costruttore, inizializzare i puntatori
// prev e next a NULL e' pericoloso, non era stato inserito
// il codice i gestione del caso particolare
/******************************/
// Costruttore
ListElement :: ListElement() {
    next = this;
    previous = this;
     value = 0;
}
int ListElement :: ReadValue() {
    return value;
}
ListElementPtr ListElement :: ReadNext() {
    return next;
}
ListElementPtr ListElement :: ReadPrevious() {
    return previous;
}
void ListElement :: WriteValue(int newvalue) {
    value = newvalue;
}
void ListElement :: WriteNext(ListElementPtr newnext) {
    next = newnext;
}
void ListElement :: WritePrevious(ListElementPtr newprevious) {
    previous = newprevious;
}
/***************************************/
// per inserire un elemento nella lista doppiamente concatenata
// occorre aggiornare 4 puntatori. Col trucco precedente del primo
// elemento auto-puntante non occorre gestire codice speciale
/***************************************/
// Funzione inserimento
ListElementPtr insert(int e, ListElementPtr This) {
ListElementPtr New;
ListElementPtr Succ;
New = new ListElement;
Succ = This->ReadNext();
New->WriteValue(e);
This->WriteNext(New);
New->WritePrevious(This);
Succ->WritePrevious(New);
New->WriteNext(Succ);
    return This;
}
void main(void) {
/*********************************************************/
/* ORRORE un puntatore inizializzato e NULL e poi usato  */
/*********************************************************/
    // ListElementPtr root = NULL;
ListElementPtr root = new ListElement;
    ListElementPtr l;
    int i;
    do {
     cout << "Introduzione valore: ";
     cin >> i;
/*****************************************************/
//  la funzione che hai scritto ti fa avere un elemento
//  testa-di-lista con contien zero come valore, dato che
//  il costruttore lo imposta cosi'
//  inoltre la condizione i fine ciclo comporta
//  comunque l'inserimento di un elemento con valore 0
/*****************************************************/
     root = insert(i, root);
    }while(i != 0);
    // stampa
/*******************************************/
// Invertito il ciclo da while a do-while
// cambiata la condizione di arresta el ciclo
/*******************************************/
  l = root;
     do {
        cout << "Valore estratto:" << l->ReadValue() << endl;
        l = l->ReadNext();
  } while(l != root);
/*************************************/
//
// per esercizio pulire la memoria prima di uscire
//
/*************************************/
   getch();
}
Penso che  col tuo codice e con i commenti inseriti il tutto funzioni
e sia abbastanza chiaro.
Ribadisco la mia osservazione dell'altra volta di "implementazione debole"
della struttura dati. Per quello che fanno le funzioni Readxxx e Writexxx
tanto vale rendere la funzione insert "friend" della classe.
Il fatto che tu abbia usato un aggiornamento dei puntatori fatto cosi'
male denota che non hai sprecato mezzo foglio per disegnare le freccine
con i rettangoli come si conviene alle prime manipolazioni dei puntatori.
Prova ad usare il debugger prima di consultare
il newsgroup, e vota per it.comp.programmare.win32



....
Spero di non essere stato troppo breve.
Ciao,
Giamby
--
--
 Giambattista Bloisi - Amiga & pOS *Giamby* software developer
      Coordinatore giornale studentesco di ScuolaItalia
                 e-mail: 
gia...@geocities.com

Grazie dell'aiuto
Ho ancora molto da studiare e i vostri suggerimenti mi sono serviti molto.
Ciao
                Pevar...





/*************************************/
Per pulire la memoria pensavo di utilizzare l'istruzione delete[]
all'interno di una funzione a cui passo l'indirizzo iniziale della lista.
La funzione esegue il delete[] ad ogni elemento della lista.
Oppure non so, delate[] this all'interno del distruttore di ogni oggetto.
Lo so che forse sto' sparando delle cavolate.
Ti ringrazio se vorrai rispondermi.
Ciao
                                        Pevar...

...

Insisti nel non leggere il manuale prima di scrivere.
"The operator new() and  operator delete() functions allow a user to
take over allocation and deallocation of individual abjects;
operator new[] and operator delete[] serve exaclty the same role for
the allocation and deallocations of arrays"
Biarne Stroustrup,The C++ Programming language",3rd ed,Addison-Wesley, pag
423.
Ora, senza tradurre pedissequamente, l'operatore delete[] si usa
quando e' stato allocato una array di elementi dal free-store con
l'operatore new[]. Ora, nel tuo caso, hai allocato una manciata di
SINGOLI elementi dal free-store, con tante operazioni di new Elemento,
e tante operazioni di new non equivalgono ad un array.
Sicuramente l'operatore delete (e basta) deve essere invocato da qualche
parte. Puoi sempre costruire una funzione apposita che cancelli gli
elementi della lista. Attento pero' con la lista non doppia.
Se hai due elementi ed applichi delete su un puntatore al primo elemento,
chi mai ti dara' un puntatore al secondo elemento? Detto grossolanamente,
in certe strutture dati la canellazione va fatta dalla coda.
L'ulizzo dell'operatore delete (e basta) nel distruttore di un elemento
della lista puo' portare ad una soluzione elegante del problema della
deallocazione di una lista "semplice". Ovviamente delete va applicato
ricorsivamente al puntatore pNext, e si deve controllare se l'oggetto
che si cancella non sia NULL, come condizione di terminazione della
ricorsione:
 void operator delete(void * pObj,size_t cBytes){
  if (reinterpret_cast<unsigned long>(pObj)){
   // following line only for debug
   //cout<<"deleting Obj: "<<pObj<<"val :"<<((unsigned long
*)pObj)[1]<<endl;
   delete (static_cast<Element *>(pObj))->m_pNext;
   // following line only for debug
   // cout<<"object deleted"<<endl;
  };
 };
Probabilmente Alex Martelli avra' occasione di criticare i miei cast,
che tradiscono una formazione C. Comunque ho testato il precedente
codice.


Trascurate il codice precedente e considerate il seguente.
void operator delete(void * pObj,size_t cBytes){
  if (reinterpret_cast<unsigned long>((static_cast<Element
*>(pObj))->m_pNext)){
      delete (static_cast<Element *>(pObj))->m_pNext;
      ::delete pObj;
      std::cout<<"deleting Obj: "<<pObj<<std::endl;
  } else {
     std::cout<<"deleting Obj: "<<pObj<<std::endl;
     ::delete pObj;
  }
};
Mi scuso con il newsgroup per le disinformazioni fornite.