lunedì 23 dicembre 2013

Esercitazione su ereditarietà e polimorfismo e testi di esami passati

Disponibile una nuova esercitazione su ereditarietà e polimorfismo e alcuni testi di esami degli anni precedenti.

12 commenti:

  1. Qualcuno ha svolto gli esercizi numero 5 e 6? Io ho avuto qualche difficoltà.

    RispondiElimina
  2. A questo indirizzo https://dl.dropboxusercontent.com/u/15714195/prg1314/exe/es13_06.zip trovi una possibile implementazione della classe IntegerSet.

    RispondiElimina
  3. salve professore, vorrei farle alcune domande sulla classe numero 1, gerarchia persona studente:

    nella mia classe base ho questi due costruttori:

    - Persona() { name = "_"; surname = "_"; cdf = "_"; cout << "\ncostructor base\n"; }

    - Persona::Persona(string s1,string s2,string s3) : name(s1), surname(s2), cdf(s3) {
    cout << "\n-------- Creata parte base Persona.\n";
    }


    nella mia classe derivata ho questi due costruttori, che inizializzano anche la parte base degli oggetti derivati:

    - Studente() { Persona(); matr = "_"; cdl = "_"; }

    - Studente::Studente(string base1,string base2,string base3, string der1,string der2) :
    Persona(base1,base2,base3), cdl(der1), matr(der2) {

    cout << "\n------ creato oggetto classe derivata";
    }


    e poi ho messo solo nella classe base un distruttore virtual:

    virtual ~Persona() { cout << "\n-------- destructor\n"; }



    se nel main creo:

    un oggetto Persona p(--------), stampa 1 volta destructor;

    un oggetto Persona p(-------), Studente s(-------); stampa 2 volte destructor;

    un oggetto Persona p(-------), Studente s; stampa 3 volte destructor

    se invece creo puntatori della classe base o derivata, non stampa destructor.


    avrei alcuni dubbi,
    intanto volevo capire se il distruttore e i costruttori cosi potessero andare bene.

    poi perchè se creo un oggetto Studente s, senza parametri stampa due volte destructor, invece se lo creo Studente s(parametri) lo stampa solo una volta.

    e poi perchè se creo puntatori della classe base o derivata non stampa destructor.

    spero di essere stato, chiaro cordiali saluti.

    RispondiElimina
  4. C'e' un piccolo errore nel costruttore Studente() in cui usi male la sintassi dei : e che per come e' scritto crea un'oggetto temporaneo Persona ogni volta che crei uno studente (da qui la differenza di comportamento quando crei oggetti Studente con e senza parametri).
    Quando crei oggetti dinamicamente (con la new) vengono invocati i costruttori. I distruttori non vengono chiamati automaticamente a fine main ma solo se chiami la delete.

    RispondiElimina
  5. Quindi mi scusi professore, il costruttore studente() come sarebbe meglio farlo, dovrei semplicemente utilizzare la sintassi degli inizializzatori con i : ?
    lo avevo fatto in quel modo per inizializzare anche la parte base di un oggetto derivato.
    e poi non ho capito bene il discorso sul distruttore, quando io faccio classi semplici diciamo, in cui non uso array dinamici per esempio, e non uso da nessuna parte la new, il distruttore come bisogna farlo?

    RispondiElimina
  6. Nel costruttore che hai scritto Studente() { Persona(); matr = "_"; cdl = "_"; } l'istruzione Persona(); seppur sintatticamente corretta non fa assolutamente nulla. Quando viene eseguito il codice del costruttore gia' e' stato chiamato il costruttore Persona() per inizializzare la parte Persona di Studente dato che quello e' il comportamento standard e non perche' tu scrivi Persona();
    I distruttori devi implementarli esclusivamente quando vuoi eseguire del codice prima che venga distrutto l'oggetto. Nella maggior parte dei casi non e' necessario implementarli.

    RispondiElimina
  7. Perfetto, tutto chiaro. Grazie mille

    RispondiElimina
  8. buon giorno professore, avrei da farle una domanda, su un programma di un esame passato, "registro chiamate"

    il testo, tra le altre cose, chiede di fare tre metodi per aggiungere, una chiamata ricevuta, una chiamata persa e una chiamata effettuata.

    questi metodi li potrei fare, facendo inserisce in ognuno di essi i dati corrispondenti all'oggetto chiamata ricevuta, effettuata o persa.
    e poi fare una new utilizzando il costruttore relativo all'oggetto, passando come parametri le cose appena inserite.

    supponendo che abbia fatto un metodo virtual per ognuna delle classi che mi permette di inserire i dati degli oggetti chiamata ricevuta,effettuata e persa.
    un altra scelta potrebbe essere quella di fare un unico metodo polimorfo per aggiungere chiamata, in cui, come parametro passo un puntatore alla classe base,
    e poi nel main con con un puntatore alla classe base, faccio la new corrispondente, uso il metodo per inserise i dati, e poi aggiungo al registro chiamate col metodo polimorfo.
    quest'ultima scelta, chiaramente piu' semplice di fare tre metodi separati, non so se sia opportuna visto il testo dell'esercizio.

    ora, supponendo che invece voglia fare tre metodi separati per aggiungere chiamate ricevute, effettuate, e perse al registro chiamate, e supponendo che abbia fatto sempre quel metodo virtual che mi permette di fare inserimenti per ognuno di questi oggetti.

    questo è un esempio del metodo che vorrei fare, che però chiamaramente cosi non funziona:

    nella classe base ho tra i dati:
    Chiamata* chiamata[100]; // dove Chiamata è la classe da cui derivano le altre chiamate

    void RegChiamate::aggiungi(Chiamata *c) {

    for(int i=0; i!=100; ++i) {
    if(chiamata[i] == 0) {
    chiamata[i] = new ChiamataRic; //err
    chiamata[i]->inserisci();
    corrente = i;
    return;
    }
    }
    }

    metodo che non va bene ovviamente per via della new, vorrei sapere come potrei fare per fare questo metodo in questa maniera, anzichè fare come il primo metodo che ho proposto, quello dove utilizzo il costruttore inserendo manualemente prima i dati

    RispondiElimina
  9. nell'ultimo metodo che ho scritto ho dimenticato a levare il parametro Chiamata *c

    RispondiElimina
  10. Se ho capito bene il metodo aggiungi() aggiunge un oggetto ChiamataRic, il metodo inserisci() e' un metodo virtual che tramite un puntatore a Chiamata chiama il metodo inserisci dell'oggetto corrispondente (che potrebbe essere di tipo ChiamataRic, ChiamataEff e ChiamataPersa). Se e' cosi' l'approccio seguito e' corretto.
    Io comunque farei un unico metodo aggiungi a cui passo un puntatore a Chiamata che all'interno chiama la tua inserisci virtual e inserisce l'oggetto nel registro. Quindi creerei l'oggetto ChiamataRic, ChiamataEff o ChiamataPersa all'esterno del metodo con la new e chiamerei il metodo aggiungi (passandogli il puntatore all'oggetto creato) per valorizzarlo e inserirlo nel registro.

    RispondiElimina
  11. l'ultimo metodo che ha proposto è quello che ho seguito per risolvere, solo che lo avrei voluto fare anche in un altra maneira.

    purtroppo per errore mio, ha frainteso solamente il metodo virtual inserisci(), che per come l'ho fatto io, mi fa inserire solamente i dati dell'oggetto corrispondente.

    la cosa che avrei voluto fare per esercizio era questa:

    fare tre metodi separati per aggiungere chiamate.
    void RegChiamate::aggiungiRicevute() {..}
    void RegChiamate::aggiungiEffettuate() {..}
    void RegChiamate::aggiungiPerse() {..}

    in particolare per esempio per aggiungere una chiamata ricevuta,vorrei fare un qualcosa di simile a ciò che avevo scritto che però non andava bene, cioè:
    con il ciclo for scorro, appena ne trovo uno vuoto aggiungo la chiamataRicevuta;
    per aggiungere la chiamata ricevuta, intendo con fare la new con il tipo dell'oggetto corrispondente, e utilizzare il metodo virtual di cui sopra, che mi permette di inserire i dati della chiamata ricevuta.

    il problema è questo, per poter fare
    chiamata[i]->inserisci();

    chiaramente prima devo aver reso valido,l'oggetto corrispondente a quell'indice, cosa che avrei voluto fare, con la riga precedente che sarebbe
    chiamata[i] = new ChiamataRic;
    //ed è qui l'errore suppongo

    volevo sapere se mi conviene usare completamente un altro approccio, o se si può fare qualcosa di diverso con la new.

    avevo pensato di fare una new utilizzando il costruttore dell'oggetto corrispondente, ma poi il metodo aggiungi sarebbe troppo lungo, perchè prima dovrei inserire manualmente tutti i dati dell'oggetto per inizializzare il costruttore

    RispondiElimina
  12. Non capisco perche pensi che chiamata[i] = new ChiamataRic debba crearti problemi. Se chiamata è un array di puntatori a Chiamata puoi tranquillamente assegnare a chiamata[i] ciò che torna new ChiamaRic.

    RispondiElimina