1.9k likes | 2.04k Views
Introduzione alla Object Oriented Programming, OOP. E.Mumolo. DEEI mumolo@univ.trieste.it. Prima parte: il C++ come estensione del C. Paradigmi di programmazione. Modello procedurale Il mondo della programmazione viene visto come un sistema di processi Modello ad oggetti
E N D
Introduzione alla Object Oriented Programming, OOP E.Mumolo. DEEI mumolo@univ.trieste.it
Paradigmi di programmazione • Modello procedurale • Il mondo della programmazione viene visto come un sistema di processi • Modello ad oggetti • Il mondo viene visto come un sistema di cose • Alcuni temi tipici del modello procedurale: • Processi • Sequenze di esecuzione • Diagrammi di flusso • Programmazione top-down • Programmazione strutturata • Algoritmi=strutture dati + programmazione strutturata • Linguaggi strutturati:Fortran, Cobol, Basic, Pascal, C … • Operazioni effettuate sui dati inviate alle procedure e alle funzioni sistemi software validi ed efficienti per basse complessità MA : ad alte complessità si evidenzia un cedimento del sistema • Una modifica di una struttura dati ha grandi effetti sul sistema à campo di visibilità delle strutture dati • Riusabilita’ nei lnguaggi procedurali
Struttura di un programma • Spaghetti code • Programmazione strutturata
Il linguaggio C++ • sviluppato alla AT&T da B.Stroustrup (1980) da una idea di Simula65. • estensione del C • astrazione dei dati • Pro: • estensione rispetto al linguaggio C • Piu adatto del C a progetti complessi • prestazioni elevate nonostante la complessità • molteplici produttori • Contro: • linguaggio complesso (non elimina le ridondanze di C, anzi ne introduce) • linguaggio ibrido: consente di continuare sviluppare codice tradizionale in C • non aiuta a programmare ad oggetti • recupero manuale della memoria (Garbage Collection)
Estensione del C • commenti: delimitati da // per commenti su una riga, oltre a /* … */ più adatti a commenti su più righe • costanti: variabili di sola lettura à specificatore const
àfor(int i=0; i<N; i++) {...} • Dichiarazioni variabili: non solo all’inizio del programma, ma anche nei cicli • Definizione di struttura, enum, unione
Riferimenti (specificatore &) • sinonimi (alias) per lo stesso oggetto • il tipo dell’oggetto determina il tipo del riferimento • puo’ essere visto come un tipo speciale di puntatore • deve essere inizializzato e agisce solo su un oggetto • uso principale: argomenti e risultati di una funzione
Uso dei riferimenti come argomenti e risultati di funzione senza passare esplicitamente l’indirizzo //opp4.cpp #include <iostream.h> int incrementa(int &val) { val++; if(val>65000) val=65000; return 0; } int incrementa(int &val, int v) { int t=v; t++; if(t>65000) t=65000; return t; } main() { int i=7; cout << " i=" << i; incrementa(i); cout << " i=" << i << " i=" << incrementa(i,i) << " i=" << i << '\n'; } Output: i=7 i=8 i=9 i=8
argomenti di default in una funzione • specificatore “inline”: inline void funzione(..) sostituisce il codice della funzione alle chiamate di funzione. : attenzione alla crescita delle dimensioni del codice! • campo di visibilità delle dichiarazioni • identificatori dichiarati nelle funzioni sono visibili dalla dichiarazione fino alla fine della funzione • identificatori globali sono visibili dalla dichiarazione alla fine del file • identificatore locale maschera un identificatore globale con un stesso nome • Scope o operatore di visibilità “::” à specifica la variabile da utilizzare • identificatori locali non sono visibili all'esterno della funzione • l’identificatore “::var” identifica la variabile globale • blocco: sequenza di istruzioni tra graffe
una funzione e’ un blocco • visibilita’ locale: identificatori definiti all’interno di un blocco • visibilita’ a livello di file: funzioni e identificatori definiti fuori da tutte le funzioni • visibilita’ degli identificatori di blocchi annidati • visibilita’ delle etichette: nel corpo della funzione a cui il blocco appartiene
Allocazione di oggetti dinamici nella memoria libera • Operatore new: argomento tipo dell’oggetto e ritorna un puntatore all’oggetto • New restituisce un puntatore nullo in caso di errore • Per allocare un array indicare tipo e numero di elementi • Operatore delete: rilascia un oggetto in memoria libera • Per rilasciare un array: specificatore []
//esempio di programmazione C++ : bubble sorting //oop13.cpp #include <stdio.h> #include <iostream.h> #include <stdlib.h> #include <string.h> float arry[10]={15.5, 44, 0.5, -1.5, 65, 0.0, 55.5, 67.0, 5, 3}; struct sistema { float *aa; int ord; }; const int ord=10, ord1=5, ord2=4; // variabili non modificabili! int bubble(float *, int N=ord); void scambia(float &, float &); // passaggio per riferimento void stampa(int); void stampa(float *a, int n=ord); main() { sistema SS[ord1]; // definisce l'array SS di 5 strutture 'sistema' sistema *sis = new sistema[ord2]; // alloca in mem. lib. un array di 4 strutture int nl;
//carica le strutture for(short i=0;i<ord1;i++){ SS[i].aa = new float[ord]; //alloca nella memoria libera for(short j=0; j<ord;j++) SS[i].aa[j]=float(random(100)); } for(short i=0;i<ord2;i++){ sis[i].aa = new float[ord]; for(short j=0; j<ord;j++) sis[i].aa[j]=float(random(100)); } //primo caso cout << "\nPrimo caso“ << endl; for(short i=0;i<ord1;i++){ printf("\n\nArray originale %d:\n", i); for(short j=0;j<ord;j++) cout << SS[i].aa[j] << " "; cout ; // short nl=bubble(SS[i].aa); // argomento di default printf("\nArray ordinato (nr. cicli=%d):\n", nl); for(short j=0;j<ord;j++) cout << SS[i].aa[j] << " "; cout ; // short } cout << "\n\nSECONDO CASO“ << endl; for(short i=0;i<ord2;i++){ printf("\n\nArray originale %d:\n", i); stampa(sis[i].aa); nl=bubble(sis[i].aa); //argomento di default: l'ordine dell'array stampa(nl); //overloading di funzioni e valori default stampa(sis[i].aa); //overloading di funzioni e valori default } }
int bubble(float *A, int N) { char *flag="notsorted"; int nloop=0; while(!strcmp(flag, "notsorted")){ flag="sorted"; nloop++; for(short i=0;i<N-1;i++) if(*(A+i) > *(A+i+1)){ //A[i] e' *(A+i) scambia(*(A+i),*(A+i+1)); //passa per riferimento!! flag="notsorted"; } } return nloop; } void scambia(float &a, float &b) // il compilatore passa l'indirizzo delle var. { float temp=a; a=b; b=temp; } void stampa(int n) { printf("\nArray ordinato (nr. cicli=%d):\n", n); } void stampa(float *a, int n) { for(short j=0;j<n;j++) cout << a[j] << " "; cout ; // definizione short }
Sovrapposizione delle funzioni (overloading) • stesso nome per funzioni diverse, che devono avere argomenti diversi in numero e tipo • lo stesso nome deve avere lo stesso tipo di risultato
Seconda parte:Il C++ come linguaggio di programmazione ad oggetti
Il modello ad oggetti • Alcuni temi tipici • Dalla astrazione funzionale alla astrazione dei dati • Il mondo e’ modellato non come processi, ma cose • Societa’ di entita’ attive, cooperanti, riusabili • Progettazione bottom-up non top-down • Modelli di classi, sottoclassi, istanze di oggetti • Linguaggi ad oggetti: Simula, Smalltalk, Eiffel, C++, Java • OOP: incapsulamento e mascheramento dei dati • limitazione del campo di visibilità delle procedure che manipolano i dati • dati e procedure inseparabili à oggetto • procedure di un oggetto (metodi) attivate inviando messaggi all'oggetto • gli oggetti inviano messaggi ad altri oggetti • punti fondamentali: tipi di dati astratti, classe, oggetti, incapsulamento, gerarchie di tipi (sottoclassi), ereditarietà, polimorfismo
OOP focalizza l'attenzione sui dati da manipolare, piuttosto che sulle procedure • obiettivi della progettazione e realizzazione di software mediante OOP : • migliorare la produttività del programmatore • aumentare la versatilità e la riutilizzazione del software • controllare la complessità • diminuire il costo di manutenzione • oggetto = contenitore che racchiude dati e funzioni che gestiscono i dati • Information hiding: capacità di oggetti di racchiude dati per eliminare accessi indebiti • interfaccia dell'oggetto: indica e pubblicizza le operazioni autorizzate ad accedere i dati • implementazione delle funzioni (codice): è in genere nascosto all'interno dell'oggetto • un oggetto riceve delle richieste e risponde alle stesse
classe = definizione astratta delle caratteristiche degli oggetti • gli oggetti sono tra loro distinti anche se derivano dalla stessa classe • creazione di un oggetto: definizione di una variabile appartenente ad una determinata classe, o definizione di un oggetto di tipo classe nella memoria libera • l'interfaccia di una classe è costituita da tutto ciò che non è nascosto in un oggetto. L'interfaccia resta in genere inalterata nel tempo • operare con gli oggetti : • divisione in componenti che contengono dati e procedure che operano sui dati • un oggetto contiene quindi sia la struttura dati che le procedure (metodi) che definiscono il comportamento dell'oggetto stesso • le strutture dati sono nascoste all'interno degli oggetti • il mondo esterno comunica con i oggetti inviando loro delle richieste • (messaggi) per informare l'oggetto su quello che deve essere fatto non su come viene fatto • suddivisione del software in classi: un sistema di archiviazione viene chiamato classe archivio con struttura dati e metodi per modificare dati • operare con gli oggetti : • ogni procedura e’ associata ad una classe • se si aggiungono funzioni di un insieme esistente, si può creare una sottoclasse, creare una nuova classe, o aggiungerle alla classe esistente • struttura gerarchica di tipi e sottotipi mediante scomposizione top-down o bottom-up: identificazione e rappresentazione dei dati piu’ importanti del sistema tramite classi.
Classe base (componente complesso) Classe base (componente minimo) Sottoclassi (componenti via via piu’ complesse) Sottoclassi (componenti minime) Automobile Specializzazione • Top-down:esempio • Bottom-up: carrozzeria ruota motore veicolo Generalizzazione Veicolo a motore Veicolo senza motore aereo auto moto taxi
Campo variabili Metodo1 Metodo2 Metodo3 var. tua var. mia var. a Puntatore ‘this’ Puntatore ‘this’ Codice dei metodi Classi privato pubblico • Una classe (ADT): class casa { ... }; • Un oggetto e’ una istanza della classe: casa a; • Piu’ oggetti hanno diversi valori delle variabili e stesso comportamento • Piu’ oggetti hanno diverse variabili, il codice e’ rientrante: casa a, mia, tua; • mia.metodo1; //attiva metodo1 mediante l’invio dell’indirizzo di mia al codice di metodo1 • Comunicazione tra oggetti tramite invio di messaggi di attivazione
Campo variabili Metodo1 Metodo2 Metodo3 Campo variabili Metodo1 Metodo2 Metodo3 Campo variabili Metodo1 Metodo2 Metodo3 Comunicazione tra oggetti Chiama tua.metodo2 a tua Chiama mia.metodo1 mia
In conclusione • Il mondo visto come sistema ad oggetti: societa’ di entita’ attive, cooperanti, riutilizzabili • Progettazione bottom-up • Ereditarieta’, polimorfismo • Visione globale
Le classi in C++ • una classe è il modello-prototipo-al quale si conformano gli oggetti che istanziano la classe • struttura di una classe: class nome_classe { private: // dati e metodi accessibili solo ai metodi della classe. Sono quindi nascosti all’esterno (non accessibili direttamente) protected: // privati all’esterno; dati e metodi accessibili solo all’interno della classe e tramite classi derivate public: // dati e metodi accessibili pubblicamente a tutte le funzioni dello stesso scope dell’oggetto }; • le componenti funzionali dichiarati in una struttura possono essere definite all'interno, (inline) o all'esterno • una classe introduce un nuovo tipo di dati • lo specificatore private è implicito • scope di una classe: visibilita’ dei componenti (funzioni e variabili) • sintatticamente la classe puo’ essere vista come una estensione di struct
il tipo di dato definito con le classi, in cui cioè la struttura interna è inaccessibile, e dal quale si possano istanziare oggetti manipolabili solo per mezzo delle operazione associate, è detto “tipo di dato astratto” (ADT) • esempio di tipo di dato astratto “contatore” //file contatore.h class contatore { private: unsigned int valore; public: contatore(); //un costruttore void incrementa(); void decrementa(); void set(unsigned int n); unsigned int val(); } • l'implementazione della classe può essere realizzato in un file separato o inline
Osservazioni • i metodi vengono attivati inviando all'oggetto un messaggio con il nome del metodo usando l’operatore punto “.”: • c1.incrementa();//invia all’oggetto c1 la richiesta di attivare //incrementa • c1.visualizza();//analogamente • Puntatore ad oggetto: operatore freccia a destra “->”:
Osservazioni (cont.) • invio di dati ad una procedura vs. invio di un messaggio all'oggetto • ma: quando un oggetto riceve un messaggio, determina come deve essere elaborato il dato sottostante usando i metodi associati al dato • non posso elaborare direttamente i dati! Es. c1.valore non è possibile • i metodi possono essere definiti anche in più file • la classe contatore potrebbe anche essere realizzata con una struttura: #include <stdio.h> struct contatore { unsigned int valore; }; main() { contatore c1, c2; c1.valore++; c2.valore++; } • ma: in questa forma i dati sono pubblici e la funzione principale accede direttamente al dato sottostante
Classi annidate (nested) • classe interna o nested: definita all’interno di un’altra classe • visibilita’:all’interno dello scope della classe che la contiene • classi definite all’interno di una funzione (scope locale) à visibile solo all’interno della funzione
Costruttori e distruttori delle classi • necessità di inizializzare le variabili • ciclo di vita di una variabile locale: nello scope in cui e’ definita • ciclo di vita di una variabile dinamica: programma • quando viene generata una variabili di tipo classe, si attiva automaticamente una funzione che inizializza le variabili della classe: costruttore • quando la variabile termina il suo ciclo di vita viene attivata automaticamente -se disponibile- una funzione di eliminazione: distruttore (ad esempio delete di variabili nella memoria libera) • costruttore: funzione con lo stesso nome della classe • non richiede tipo di ritorno • puo’ avere una lista di argomenti di inizializzazione • attivata automaticamente quando si crea un'oggetto con new • sono possibili costruttore diversi, che devono avere liste di argomenti diversi • costruttore di default: senza argomenti.
Costruttore di default • funzione senza argomenti formali • chiamata dal compilatore quando viene definita una variabile senza inizializzazione speciale • nel caso si richeda una inizializzazione speciale à costruttore con argomenti
Costruttore con argomenti: il costruttore da utilizzare dipende dalla lista degli argomenti • variabili dinamiche stringa *ps=new stringa(“iniziale”); //new attiva il costruttore. Se new fallisce, ps punta a //NULL e il compilatore NON attiva il costruttore ps->stampa(); ...
Costruttore con argomenti opzionali: #include <iostream.h> class stringa { int len; char *str; public: stringa(int a=0, char ch=' ') { len=a; str[len]='\0'; if(a>0) { str=new char[len+1]; for(int i=0; i<len; i++) str[i]=ch; } } };
distruttori: funzioni che gestiscono la distruzione del contenuto di oggetti • utilizza il nome della classe preceduto da dal carattere ~ • attivata automaticamente al termine del ciclo di vita di un'oggetto • in genere usati per rilasciare la memoria dinamica oppure per salvare • informazioni su file • annulla quello fatto dal costruttore • libera la memoria addizionale utilizzata dell'oggetto • libera le risorse locale e chiude i file aperti • libera la memoria dinamica • il distruttore, come ogni metodo, può essere chiamato esplicitamente stringa *p=new stringa("nome"); p-> stringa:: ~stringa();
//esempio diADT nodo ADT lista //oop23.cpp #include <stdio.h> #include <iostream.h> #include <stdlib.h> #include <string.h> class nodo { private: nodo *next; int valore; public: nodo(){next=NULL;} void loadn(nodo *a){next=a;} void loadv(int a){valore=a;} nodo *getn(){return next;} int getv(){return valore;} }; class lista { private: nodo *head; public: lista(){head=NULL;} //costruttore ~lista(){} //distruttore: lasciato per esercizio! void insert(int n); //at the top void add(int n); //at the bottom int hremove(); //toglie dalla cima int tremove(); //toglie dalla coda void type(); //visita dalla cima e stampa il contenuto };
void lista::insert(int n) { nodo *temp; temp=new nodo; temp -> loadv(n); temp -> loadn(NULL); if(head) {temp -> loadn(head); head=temp;} else {temp -> loadn(NULL); head=temp;} } void lista::add(int n) { nodo *temp, *prec; if(head){ temp=head; while(temp){ prec=temp; temp=temp->getn(); } prec->loadn(new nodo); prec=prec->getn(); prec->loadv(n); prec->loadn(NULL); } else { head=new nodo; head -> loadv(n); head -> loadn(NULL); } }
int lista::hremove() { int n; if(head){ n=head->getv(); head=head->getn(); return(n); } else return(65536); //65536 means empty } int lista::tremove() { int n; nodo *temp, *prec; temp=head; if(head){ while(temp->getn()) { prec=temp; temp=temp->getn(); } n=temp->getv(); prec->loadn(NULL); delete(temp); return(n); } else return(65536); //65536 means empty }
void lista::type() { nodo *temp; temp=head; while(temp){ cout << temp->getv() << '\n'; temp=temp->getn(); } } main() { lista L1, L2; int n; for(int i=0; i<5; i++) L1.insert(i); cout << "L1:" << '\n'; L1.type(); L2.add(10); L2.add(100); cout << "L2:" << '\n'; L2.type(); L1.add(5); L1.add(6); L1.add(7); cout << "L1 after add" << '\n'; L1.type(); cout << "L1hrem " << L1.hremove() << '\n'; cout << "L1hrem " << L1.hremove() << '\n'; while( (n=L2.tremove())!=65536) cout << "L2 trem " << n << '\n'; while( (n=L1.hremove())!=65536) cout << "L1 hrem " << n << '\n'; }
//esempio diADT Albero // File Albero.h #define NULL 0 struct nodo { int dato; nodo *sin,*des; }; class Albero { public: Albero() { radice = NULL; } protected: nodo* radice; }; //file AlbRic.h #include "Albero.h“ #include <iostream.h> class AlbRic : public Albero { public: AlbRic() {} ~AlbRic() { Cancella(radice); } void Inserisci(int i) { Aggiungi(i,radice); } void Visita(){ Differito(radice); } private: void Cancella(nodo * &p); void Aggiungi(int i, nodo* &p); void Differito(nodo *p); };
// File oop24.cpp #include "AlbRic.h" void AlbRic::Cancella(nodo* &p) { if(p!=NULL) { Cancella(p->sin); Cancella(p->des); delete p; } } void AlbRic::Differito(nodo * p) { if(p!=0) { Differito(p->sin); Differito(p->des); cout << p->dato << " "; } } void AlbRic::Aggiungi(int i, nodo* &p) { if (p == NULL) { p = new nodo; p->dato = i; p->sin = NULL; p->des = NULL; } else if (i < p->dato) Aggiungi(i,p->sin); else if (i > p->dato) Aggiungi(i,p->des); } void main() { AlbRic a; for(int i=0;i<10;i++) a.Inserisci(i); a.Visita(); }