martedì 18 gennaio 2011

Possibili soluzioni esercitazione 8

Disponibile una possibile soluzione dell'esercitazione 8. Come sempre, commenti e richieste di chiarimenti sono benevenuti.

16 commenti:

  1. Prof.re,
    perchè nelle funzioni get dopo i parametri ha inserito const?
    Esempio:
    int getOra() const;
    int getMin() const;
    int getSec() const;

    RispondiElimina
  2. Prof se creo due oggetti Appuntamento a,b perché quando eseguo l'istruzione:
    Appuntamento b(a);
    Mi esegue tutto tranquillamente anche se il prototipo del costruttore prevede :
    Appuntamento(string="", int=8, int=0, int=8, int=30);

    RispondiElimina
  3. ho svolto l'esercizio 5 (Appuntamentoe time) , struttuando il programma su più file , ho :
    Appuntamento.cpp
    Appuntamento.h
    Time.cpp
    Time.h

    Nel file appuntamento.h ho dichiarato il costruttore nel seguente modo:
    Appuntamento(string = "vuoto" , Time , Time );

    ed nel file appuntamento.cpp ho scritto:

    Appuntamento::Appuntamento(string desc, Time iniziale , Time finale){
    setappuntamento(desc,iniziale,finale);}

    tuttavia in fase di compilazione ottengo il seguente errore:

    default argument missing for parameter 2 of `Appuntamento::Appuntamento(std::string, Time, Time)'

    ho risolto attribuendo un valore di default =0 nel prototipo del costruttore , a gli oggetti Time , cioè scrivendo in Appuntamento.h

    Appuntamento(string = "vuoto" , Time =0 , Time =0 );

    Adesso sembrerebbe funzionare , xò non comprendo il perché ! e vorrei sapere cosa significa attribuire ad un oggetto Time il valore 0!!

    RispondiElimina
  4. @Giuseppeian: i const mi sono scappati inavvertitamente ma sono una buona regola di programmazione... Il problema e' che ancora non ne abbiamo parlato a lezione.
    In breve se so che un metodo non deve modificare dati membro della classe (come nel caso delle funzioni get) lo posso definire const e questo implica che, se inavvertitamente modifico un dato membro, il compilatore mi segnale l'errore.
    Inoltre i metodi const sono gli unici che possono essere chiamati per oggetti const, ma ne parleremo dopo.

    RispondiElimina
  5. @SalvoinZ: scrivere Appuntamento b(a); (che poi e' equivalente a scrivere Appuntamento b = a;) implica che l'oggetto b viene creato come copia dell'oggetto a a quindi non viene chiamato il costruttore ma viene fatta la copia bit-a-bit.
    La situazione e' analoga a quando passi un oggetto per valore a una funzione e viene costruito all'interno della funzione un oggetto come copia del valore passato facendo copia bit-a-bit senza chiamare costruttori.
    In queste situazioni l'unico costruttore che sarebbe chiamato sarebbe il costruttore di copie se implementato.

    RispondiElimina
  6. @Roberto: se ho intuito bene cio' che hai fatto (dato che non hai postato codice) il compilatore ti dice "default argument missing for parameter 2 of `Appuntamento::Appuntamento(std::string, Time, Time)' " perche' tu provi a costruire un oggetto nel main passando solo una string e quindi gli mancano gli ultimi due parametri. Se crei l'oggetto passando una string e due oggetti Time non ti dovrebbe dare piu' l'errore.
    L'argomento di default di tipo int per un parametro Time ha senso perche' in Time c'e' un costruttore che permette di costruire un oggetto a partire da un int quindi come parametro di default verra' usato l'oggetto Time che otterresti ad esempio con Time t(0);
    Se non era questo che cio' che intendevi posta il codice che ne riparliamo.

    RispondiElimina
  7. //Appuntamento.h
    #ifndef APPUNTAMENTO_H
    #define APPUNTAMENTO_H
    #include "time.h"
    #include
    using std::string;

    class Appuntamento{

    public:
    Appuntamento(string = "vuoto" , Time =0 , Time =0 );
    ~Appuntamento(){}
    void setappuntamento(string , Time , Time);
    Time getorai();
    Time getoraf();
    void stampa();

    private:
    string descrizione;
    Time orai;
    Time oraf;
    };

    #endif

    /*metodi della classe appuntamento file appuntamento.cpp*/

    #include "appuntamento.h"
    #include "time.h"
    #include
    using std::cout;
    using std::endl;
    using std::string;


    void Appuntamento::setappuntamento(string desc, Time iniziale , Time finale ){
    descrizione=desc;
    orai=iniziale;
    oraf=finale;
    }

    Appuntamento::Appuntamento(string desc, Time iniziale , Time finale){
    setappuntamento(desc,iniziale,finale);
    }

    Time Appuntamento::getorai(){return orai;}
    Time Appuntamento::getoraf(){return oraf;}

    void Appuntamento::stampa(){

    cout << "Appuntamento dalle: " ;
    orai.stampa();
    cout << "alle: ";
    oraf.stampa();
    cout << endl;
    cout << descrizione << endl;

    }


    //Time.h
    #ifndef TIME_H
    #define TIME_H
    #include
    class Time{

    public:
    Time(int = 0 , int = 0 , int = 0);
    ~Time(){}
    void setall(int , int , int);
    void setora(int);
    void setmin(int);
    void setsec(int);

    int getora();
    int getmin();
    int getsec();
    void stampa();


    private:
    int ore;
    int minuti;
    int secondi;
    };
    #endif // TIME_H


    //Time.cpp
    #include "time.h"
    #include
    using std::cout;
    using std::endl;
    Time::Time(int h , int m , int s){setall(h,m,s);}

    void Time::setall(int h, int m, int s){
    setora(h);
    setmin(m);
    setsec(s);
    }


    void Time::setora(int h){ h>=0 && h<=23 ? ore=h : ore=getora();}
    void Time::setmin(int m){ minuti=(m>=0 && m<=59) ? m:getmin();}
    void Time::setsec(int s){ secondi=(s>=0 && s<=59) ? s:getsec();}
    int Time::getora(){return ore;}
    int Time::getmin(){return minuti;}
    int Time::getsec(){return secondi;}
    void Time::stampa(){ cout << getora() << ":" << getmin() << ":" << getsec() << endl;}


    se in appuntamento.h anzicché scrivere Appuntamento(string = "vuoto" , Time =0 , Time =0 ); avessi scritto:
    Appuntamento(string = "vuoto" , Time , Time) quindi senza inizializzare gli oggetti time , avrei ottenuto quell'errore lì

    RispondiElimina
  8. @Roberto: non avevo notato l'ordine degli argomenti di default, indipendentemente da tutti gli altri discorsi fatti nel commento precedente una funzione come Appuntamento(string = "vuoto" , Time , Time) non puo' mai compilare perche' gli argomenti di default devono essere i piu' a destra (come caso particolare possono essere anche tutti) e non a sinistra come fatto da te.

    RispondiElimina
  9. adesso funziona , mi era sfuggito questo piccolo particolare :D , non riuscivo proprio a capire xkè non funzionasse , ora è tutto + chiaro , per il resto è strutturato bene il programma?

    RispondiElimina
  10. @Roberto: come struttura va bene, la scelta dei nomi dei metodi non e' tanto felice. Per maggiore leggibilita' spesso si usano le maiuscole per separare le parole che compongono il nome, ad esempio setOra() e getOra() invece di setora() e getora().

    RispondiElimina
  11. struct Nodo {
    int dato;
    Nodo* prec;
    Nodo* succ;
    };

    int main(){
    Nodo* curr = NULL; // Puntatore al nodo corrente
    Nodo* tmp;

    // CREA NUOVO NODO
    tmp = new Nodo;
    cout << "Inserisci dato del nuovo nodo: ";
    cin >> tmp->dato;

    if(curr == NULL){
    tmp->prec=tmp;
    tmp->succ=tmp;
    curr = tmp;
    }

    // Lo mette subito dopo curr e aggiusta i puntatori
    else {
    tmp->prec=curr;
    tmp->succ=curr->succ;
    curr->succ = tmp;
    (tmp->succ)->prec = tmp;
    }
    }
    QUESTO È il codice che ha postato nella soluzione dell'esercitazione 5numero 11 , non ho capito bene all'interno dell'else , come faccio ad aggiornare nuovamente current al nodo corrente, (che sarebbe tmp) , ed inoltre al termine del if ed else , non dovrei deallocare tmp?

    RispondiElimina
  12. @Roberto: current non c'e' bisogno di aggiornarlo al nodo appena aggiunto. In pratica si aggiustano i puntatori per inserire il nuovo nodo fra il nodo corrente e il successivo ma il nodo corrente resta invariato.
    Non devi deallocare tmp; la lista e' dinamica e ogni nodo lo crei quindi con new al momento dell'inserimento e lo deallochi solo quando devi eliminarlo dalla lista.

    P.S. La prossima volta se hai un quesito relativo all'esercitazione X pubblicalo nel post relativo all'esercitazione X e non nell'ultimo post disponibile.

    RispondiElimina
  13. quindi se ho ben capito , il primo nodo inserito è il corrente , il 2° è "l'ultimo della lista" che comunque è contcatenato con il corrente , ogno volta che inserisco un nodo , viene posto subito dopo il corrente e prima del'ultimo nodo inserito e vengono aggiustati i puntatori. Quindi quando scrivo tmp=new Nodo , al puntatore tmp attribuisco un nuovo indirizzo di un blocco di memoria dedicato a un nodo , e giustamente se lo cancellassi perderei le informazioni su quel nodo , o sbaglio?

    mentre per quanto riguarda la cancellazione , il mio nuovo nodo corrente diventa il nodo successivo a quello corrente , (naturalmente dopo aver riordinato i puntatori) , fatto ciò dealloco l'ex nodo corrente ( il quale indirizzo di memoria è stato precedentemente salvato in tmp) .

    cmq credo di aver capito !!! , ora provo a implementarla con la classe

    RispondiElimina
  14. sono riuscito a implementarla con la classe :)

    RispondiElimina
  15. void Lista::eliminaNodo(){
    if(corrente != NULL){ // se lista vuota non fa nulla
    if(corrente->getSucc() == corrente){ // caso di lista con un solo nodo
    corrente = NULL;
    } else {
    corrente->getPrec()->setSucc(corrente->getSucc());
    corrente->getSucc()->setPrec(corrente->getPrec());
    corrente = corrente->getPrec();
    }
    }
    }

    vorrei un chiarimento su questa implementazione. quando abbiamo il caso di lista con un solo elemento perché impostiamo il corrente uguale a NULL? Non si potrebbe liberare direttamente la memoria con delete?

    RispondiElimina
  16. Se abbiamo una lista con un solo elemento e dobbiamo eliminare quell'elemento allora dobbiamo settare corrente a NULL e poi liberare la memoria che occupava quel nodo. Fare solo la delete senza aggiustare corrente non sarebbe corretto.
    Comunque, dal tuo post mi sono accorto il codice pubblicato non e' del tutto corretto. A causa di una mia dimenticanza, infatti si e' creato un memory leak. Osservate che non viene mai fatta la delete e quindi i nodi che vengono eliminati dalla lista restano di fatto in memoria anche se non sono piu' accessibili. Il codice che avevo pubblicato aggiusta i puntatori, setta il nuovo valore per corrente ma non dealloca il vecchio corrente che e' appunto il nodo da eliminare. Il codice corretto e' il seguente:

    void Lista::eliminaNodo(){
    if(corrente != NULL){ // se lista vuota non fa nulla
    Nodo* old_corrente = corrente;
    if(corrente->getSucc() == corrente){ // caso di lista con un solo nodo
    corrente = NULL;
    } else {
    corrente->getPrec()->setSucc(corrente->getSucc());
    corrente->getSucc()->setPrec(corrente->getPrec());
    corrente = corrente->getPrec();
    }
    delete old_corrente;
    }
    }

    RispondiElimina