Blog del corso di Programmazione (9 CFU) tenuto da Marco La Cascia presso l'Universita' di Palermo per il corso di laurea in Ingegneria Informatica e delle Telecomunicazioni. Tratta la programmazione a oggetti in Java.
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.
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.
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.
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?
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.
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
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
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.
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
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.
Qualcuno ha svolto gli esercizi numero 5 e 6? Io ho avuto qualche difficoltà.
RispondiEliminaA questo indirizzo https://dl.dropboxusercontent.com/u/15714195/prg1314/exe/es13_06.zip trovi una possibile implementazione della classe IntegerSet.
RispondiEliminasalve professore, vorrei farle alcune domande sulla classe numero 1, gerarchia persona studente:
RispondiEliminanella 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.
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).
RispondiEliminaQuando 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.
Quindi mi scusi professore, il costruttore studente() come sarebbe meglio farlo, dovrei semplicemente utilizzare la sintassi degli inizializzatori con i : ?
RispondiEliminalo 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?
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();
RispondiEliminaI distruttori devi implementarli esclusivamente quando vuoi eseguire del codice prima che venga distrutto l'oggetto. Nella maggior parte dei casi non e' necessario implementarli.
Perfetto, tutto chiaro. Grazie mille
RispondiEliminabuon giorno professore, avrei da farle una domanda, su un programma di un esame passato, "registro chiamate"
RispondiEliminail 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
nell'ultimo metodo che ho scritto ho dimenticato a levare il parametro Chiamata *c
RispondiEliminaSe 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.
RispondiEliminaIo 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.
l'ultimo metodo che ha proposto è quello che ho seguito per risolvere, solo che lo avrei voluto fare anche in un altra maneira.
RispondiEliminapurtroppo 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
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