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ì 24 gennaio 2011
Testo esercitazione 9
Disponibile per il download il testo dell'esercitazione 9 che svolgeremo domani pomeriggio in aula informatica.
Dopo essere impazzito per almeno mezz'ora a causa dell'ordine dei due #include all'interno di Studente.cpp, ora ho un problemino con il distruttore della classe Studente. Se mi azzardo a decommentare in Studente.cpp il distruttore non capisco perchè mi da errore di compilazione.
L'ordine dei due include in Studente.cpp deve essere irrilevante se il codice e' scritto correttamente. Nel tuo caso hai avuto quel problema perche' hai dimenticato di includere Persona.h in Studente.h per cui rendevi l'esito positivo della compilazione dipendenti dall'ordine di compilazine dei file e dall'ordine degli include. Probabilmente anche quell'errore del doppio distruttore era legato a questo.
salve prof. nel esercizio numero 2 , dopo aver creato la classe punto2d e la classe punto3d ( che eredità l'interfaccia di punto2d) , nel metodo per il calcolo della distanza tra 2 punti nella classe punto3d , ho erroneamente riutilizzato il metodo della classe punto2d ( la quale si aspetta un punto2d) passando invece un punto3d! naturalmente a tale valore al quadrato aggiungo la differenza tra le Z al quadrato e poi ne faccio la radice... nonostante ciò il programma funziona xfettamente e nn da nessuno tipo di warning ! cm 'è possibile?! .. allego il file con il programma
Paragonando i due output dell'esercizio 3, noto che:
Per la composizione vengono chiamati costruttori e distruttori prima della fine del programma:
Costruttore di Point Costruttore di Point Costruttore di Circle Distruttore di Point Distruttore di Point Costruttore di Point Costruttore di Point Costruttore di Circle Distruttore di Point Distruttore di Point Costruttore di Cylinder Distruttore di Circle Distruttore di Point Distruttore di Circle Distruttore di Point Premere un tasto per continuare . . . Distruttore di Cylinder Distruttore di Circle Distruttore di Point Distruttore di Circle Distruttore di Point Distruttore di Point
Mentre per l'ereditarietà prima vengono costruiti gli oggetti e alla fine del programma viene chiamato il distruttore di ogni oggetto:
Costruttore di Point Costruttore di Point Costruttore di Circle Costruttore di Point Costruttore di Circle Costruttore di Cylinder Premere un tasto per continuare . . . Distruttore di Cylinder Distruttore di Circle Distruttore di Point Distruttore di Circle Distruttore di Point Distruttore di Point
Questo vuol dire che creando classi sfruttando la composizione si ha una migliore gestione della memoria?
@Roberto: le cose funzionano perche' un Punto3D e' un Punto2D con in piu' altre caratteristiche (la terza coordinata) che in questo contesto vengono ignorate. Non e' vero il viceversa, non puoi usare un Punto2D dove lui si aspetta un Punto3D. Ne parleremo oggi a lezione.
@Gabriele: dal punto di vista della gestione della memoria non c'e' differenza fra un oggetto che ne estende un altro con l'ereditarieta' o con la coposizione. La differenza dell'output dei tuoi programmi non ho come spiegarla, devi postare un link ai sorgenti...
salve, io ho un problema nell'esercizio 3 ... quando vado a creare le tre classi per composizione e dopo aver creato la classe punto devo creare la classe Circle che qui riporto:
class Circle{ public: Circle(Point,double); ~Circle(); void setPoint(Point); void setRaggio(double); Point getPoint(); double getRaggio(); void print();
private: Point x; double raggio; };
Il problema sta in setPoint e getPoint.... basta che metto:
void Circle::setPoint(Point p){ x = p; } Point Circle::getPoint(){ return x; } o li devo implementare in maniera diversa? la stessa cosa mi succede quando creo il cilindro e come private gli passo un cilindro e un double altezza!
Basandomi sulla sua lezione di oggi in merito alla gestione della memoria, noto che ho operato scelte differenti fra le due implementazioni delle classi.
In sintesi, nella versione riguardante l'ereditarietà ho creato un costruttore che riceve 4 double (x, y, altezza, raggio), mentre in quella concernente la composizione ho operato concettualmente come Eliseo passandogli 1 oggetto di tipo Circle e un double per l'altezza. Il miei due output potrebbero differire a causa di queste scelte? Oppure non c'entra niente? :D
@Eliseo: setPoint e getPoint come le hai scritte vanno bene. Ricorda infatti che gli oggetti vengono trattati in tutto e per tutto come variabili di tipo primitivo e quindi sei libero di assegnare un valore a un altro, far ritornare il valore a una funzione, etc... come faresti con i tipi primitivi.
@Gabriele: il fatto che la versione con la composizione ti chiama piu' costruttori e distruttori (e quindi crea piu' oggetti temporanei) non e' dovuto al fatto che usi la composizione al posto dell'ereditarieta' ma al fatto che passi oggetti per valore. Se avessi scritto per la classe Circle che usa l'ereditarieta' un costruttore a cui passi per valore un Point avresti avuto anche in questo caso la creazione e distruzione di un oggetto temporaneo di tipo Point.
Succede che l'arary non contiene i char che gli passo ma alcuni che si trovano in memoria.
String :: String() : DIM(120){ str = new char[DIM]; /*Qui dovrebbe inizializzare tutto str con caratteri di fine string*/ for (int i = 0; i < DIM; i++){ str[i] = '\0'; ...
Ho caricato tutto il progetto.dev qui: http://www.megaupload.com/?d=70FCVZY1
Il problema era nel costruttore parametrizzato in cui cercavi di chiamare quello senza parametri per allocare e inizializzare la memoria. Questa e' un'operazione che non e' consentita (non da errore in compilazione per motivi che vedremo ma non esegue il costruttore invocato) e quindi di fatto tu lavoravi su memoria non allocata con quello che cio' comporta. In pratica il costruttore parametrizzato deve essere:
String :: String(char nome[]) : DIM(120){ str = new char[DIM]; for (int i = 0; i < DIM; i++){ str[i] = '\0'; }
for (int i = 0; i < DIM; i++){ if (nome[i] != 0){ str[i] = nome[i]; } } }
Inoltre nel main inzializzavi male l'array di test frase mettendo i valori non stampabili 1, 2 e 3 al posto dei corrispondenti caratteri, doveva essere:
Dopo essere impazzito per almeno mezz'ora a causa dell'ordine dei due #include all'interno di Studente.cpp, ora ho un problemino con il distruttore della classe Studente. Se mi azzardo a decommentare in Studente.cpp il distruttore non capisco perchè mi da errore di compilazione.
RispondiElimina//main.cpp
http://nopaste.info/c4b33c76ca.html
// Persona.h
http://nopaste.info/d780c39520.html
// Persona.cpp
http://nopaste.info/47611558f0.html
// Studente.h
http://nopaste.info/5490101ef6.html
// Studente.cpp
http://nopaste.info/489d47fd76.html
O_o
RispondiEliminaOra funziona.. solo decommentando. Se levavo il commento mi dava errore segnalandomi la presenza di un doppio distruttore (?)
Boh
L'ordine dei due include in Studente.cpp deve essere irrilevante se il codice e' scritto correttamente. Nel tuo caso hai avuto quel problema perche' hai dimenticato di includere Persona.h in Studente.h per cui rendevi l'esito positivo della compilazione dipendenti dall'ordine di compilazine dei file e dall'ordine degli include.
RispondiEliminaProbabilmente anche quell'errore del doppio distruttore era legato a questo.
Ah la ringrazio, pensavo che ereditandolo non fosse necessario l'include. Correggo subito :)
RispondiEliminasalve prof. nel esercizio numero 2 , dopo aver creato la classe punto2d e la classe punto3d ( che eredità l'interfaccia di punto2d) , nel metodo per il calcolo della distanza tra 2 punti nella classe punto3d , ho erroneamente riutilizzato il metodo della classe punto2d ( la quale si aspetta un punto2d) passando invece un punto3d! naturalmente a tale valore al quadrato aggiungo la differenza tra le Z al quadrato e poi ne faccio la radice... nonostante ciò il programma funziona xfettamente e nn da nessuno tipo di warning ! cm 'è possibile?! .. allego il file con il programma
RispondiEliminahttp://rapidshare.com/files/444499730/es2.rar
Paragonando i due output dell'esercizio 3, noto che:
RispondiEliminaPer la composizione vengono chiamati costruttori e distruttori prima della fine del programma:
Costruttore di Point
Costruttore di Point
Costruttore di Circle
Distruttore di Point
Distruttore di Point
Costruttore di Point
Costruttore di Point
Costruttore di Circle
Distruttore di Point
Distruttore di Point
Costruttore di Cylinder
Distruttore di Circle
Distruttore di Point
Distruttore di Circle
Distruttore di Point
Premere un tasto per continuare . . .
Distruttore di Cylinder
Distruttore di Circle
Distruttore di Point
Distruttore di Circle
Distruttore di Point
Distruttore di Point
Mentre per l'ereditarietà prima vengono costruiti gli oggetti e alla fine del programma viene chiamato il distruttore di ogni oggetto:
Costruttore di Point
Costruttore di Point
Costruttore di Circle
Costruttore di Point
Costruttore di Circle
Costruttore di Cylinder
Premere un tasto per continuare . . .
Distruttore di Cylinder
Distruttore di Circle
Distruttore di Point
Distruttore di Circle
Distruttore di Point
Distruttore di Point
Questo vuol dire che creando classi sfruttando la composizione si ha una migliore gestione della memoria?
@Roberto: le cose funzionano perche' un Punto3D e' un Punto2D con in piu' altre caratteristiche (la terza coordinata) che in questo contesto vengono ignorate. Non e' vero il viceversa, non puoi usare un Punto2D dove lui si aspetta un Punto3D. Ne parleremo oggi a lezione.
RispondiElimina@Gabriele: dal punto di vista della gestione della memoria non c'e' differenza fra un oggetto che ne estende un altro con l'ereditarieta' o con la coposizione. La differenza dell'output dei tuoi programmi non ho come spiegarla, devi postare un link ai sorgenti...
RispondiEliminasalve, io ho un problema nell'esercizio 3 ... quando vado a creare le tre classi per composizione e dopo aver creato la classe punto devo creare la classe Circle che qui riporto:
RispondiEliminaclass Circle{
public:
Circle(Point,double);
~Circle();
void setPoint(Point);
void setRaggio(double);
Point getPoint();
double getRaggio();
void print();
private:
Point x;
double raggio;
};
Il problema sta in setPoint e getPoint....
basta che metto:
void Circle::setPoint(Point p){
x = p;
}
Point Circle::getPoint(){
return x;
}
o li devo implementare in maniera diversa?
la stessa cosa mi succede quando creo il cilindro e come private gli passo un cilindro e un double altezza!
Posto un rar che contiene entrambi i progetti sviluppati.
RispondiEliminahttp://rapidshare.com/files/444664410/Esercitazione_9.rar
Basandomi sulla sua lezione di oggi in merito alla gestione della memoria, noto che ho operato scelte differenti fra le due implementazioni delle classi.
In sintesi, nella versione riguardante l'ereditarietà ho creato un costruttore che riceve 4 double (x, y, altezza, raggio), mentre in quella concernente la composizione ho operato concettualmente come Eliseo passandogli 1 oggetto di tipo Circle e un double per l'altezza.
Il miei due output potrebbero differire a causa di queste scelte? Oppure non c'entra niente? :D
Professore nell'esercizio sulla classe String ho creato i seguenti file:
RispondiEliminaString.h
...
public:
String();
...
private:
const int DIM;
char *str;
...
String.cpp
...
String :: String() : DIM(120){
str = new char[DIM];
for (int i = 0; i < DIM; i++){
str[i] = '\0';
}
}
...
Ma non capisco perchè non mi inizializza l'array str come se fosse un array vuoto.
@Eliseo: setPoint e getPoint come le hai scritte vanno bene. Ricorda infatti che gli oggetti vengono trattati in tutto e per tutto come variabili di tipo primitivo e quindi sei libero di assegnare un valore a un altro, far ritornare il valore a una funzione, etc... come faresti con i tipi primitivi.
RispondiElimina@Gabriele: il fatto che la versione con la composizione ti chiama piu' costruttori e distruttori (e quindi crea piu' oggetti temporanei) non e' dovuto al fatto che usi la composizione al posto dell'ereditarieta' ma al fatto che passi oggetti per valore.
RispondiEliminaSe avessi scritto per la classe Circle che usa l'ereditarieta' un costruttore a cui passi per valore un Point avresti avuto anche in questo caso la creazione e distruzione di un oggetto temporaneo di tipo Point.
@luca: che significa "non mi inizializza l'array str come se fosse un array vuoto"? cosa succede quando esegui il programma?
RispondiEliminaQuesto commento è stato eliminato dall'autore.
RispondiEliminaSuccede che l'arary non contiene i char che gli passo ma alcuni che si trovano in memoria.
RispondiEliminaString :: String() : DIM(120){
str = new char[DIM];
/*Qui dovrebbe inizializzare tutto str con caratteri di fine string*/
for (int i = 0; i < DIM; i++){
str[i] = '\0';
...
Ho caricato tutto il progetto.dev qui:
http://www.megaupload.com/?d=70FCVZY1
Il problema era nel costruttore parametrizzato in cui cercavi di chiamare quello senza parametri per allocare e inizializzare la memoria. Questa e' un'operazione che non e' consentita (non da errore in compilazione per motivi che vedremo ma non esegue il costruttore invocato) e quindi di fatto tu lavoravi su memoria non allocata con quello che cio' comporta. In pratica il costruttore parametrizzato deve essere:
RispondiEliminaString :: String(char nome[]) : DIM(120){
str = new char[DIM];
for (int i = 0; i < DIM; i++){
str[i] = '\0';
}
for (int i = 0; i < DIM; i++){
if (nome[i] != 0){
str[i] = nome[i];
}
}
}
Inoltre nel main inzializzavi male l'array di test frase mettendo i valori non stampabili 1, 2 e 3 al posto dei corrispondenti caratteri, doveva essere:
char frase[] = {'1','2','3',0};
o semplicemente:
char frase[] = "123";