Fondamenti di Informatica 2 Ingegneria Informatica Docente: Giovanni Macchia a.a. 2002-2003

49
Fondamenti di Informatica 2 Ingegneria Informatica Docente: Giovanni Macchia a.a. 2002-2003

description

Fondamenti di Informatica 2 Ingegneria Informatica Docente: Giovanni Macchia a.a. 2002-2003. Classi ed Oggetti. Il C++ è un linguaggio di programmazione che fornisce gli strumenti per la programmazione ad oggetti. In C++ le unità fondamentali sono le classi . - PowerPoint PPT Presentation

Transcript of Fondamenti di Informatica 2 Ingegneria Informatica Docente: Giovanni Macchia a.a. 2002-2003

Fondamenti di Informatica 2Ingegneria Informatica

Docente: Giovanni Macchiaa.a. 2002-2003

Classi ed OggettiClassi ed Oggetti

Il C++ è un linguaggio di programmazione che fornisce gli strumenti per la programmazione ad oggetti.

In C++ le unità fondamentali sono le classi.Le classi implementano gli ADT. Gli oggetti sono istanze delle classi. Vengono create considerando le classi come il loro tipo base.

Classi ed OggettiClassi ed OggettiLa definizione di classe avviene tramite la parola chiave class e la forma è simile alla struct, di cui rappresenta l’evoluzione:class nome_classe {public:variabili e funzioni pubbliche;private:variabili e funzioni private;protected:variabili e funzioni protette;};

Classi ed OggettiClassi ed OggettiLe variabili e funzioni definite dopo la parola chiave public e prima dell’inizio della parola chiave successiva sono accessibili da altre parti del programma. Le variabili e funzioni definite dopo la parola chiave private e prima dell’inizio della parola chiave successiva sono nascoste ad altre parti del programma e possono essere usate solo da funzioni membro della classe.Vedremo più avanti il significato della parola chiave protected.Le parole chiave protected, public e private si chiamano specificatori di accesso ai membri

Classi ed OggettiClassi ed OggettiEs:class Persona {private:int stipendio;public:char cognome[30];char indirizzo[30];};Persona Ugo; // si istanzia un oggetto avente // come tipo dato la classe Persona;Altre parti del programma possono accedere a Ugo.cognome e Ugo.indirizzo ma non direttamente a Ugo.Stipendio

Classi ed OggettiClassi ed Oggetti

E’ possibile per altre parti del programma accedere e/o manipolare i membri protetti tramite le funzioni membro della classe.

Le funzioni membro di una classe definiscono il comportamento che esibiscono gli oggetti appartenenti a quella stessa classe. In C++ implementano i metodi dell’ OOP. La classe è una evoluzione della struct di C. La differenza è che i membri senza specificatore di accesso in una classe sono privati, mentre in una struttura sono pubblici.

Classi ed OggettiClassi ed OggettiEs:class counter {public:void reset( );void incr ();void decr ( );int get( );private:int val;};

funzioni membro

Classi ed OggettiClassi ed OggettiIn alcuni casi, è importante avere la possibilità di definire dei membri di una classe comuni a tutti gli oggetti della stessa classe.Per fare questo, si usa il modificatore static per dichiarare una variabile statica della classe, ovvero condivisa da tutti gli oggetti della classe.Es:class counter {public:void reset( );….private:static int val;};

Classi ed OggettiClassi ed OggettiE’ possibile dichiarare una funzione non membro o una classe come friend di una classe. In questo modo si consente alla funzione o classe friend di accedere ai membri privati delle classe.

Es;class counter {public:friend void print(int);……...};

Classi ed Oggetti: l’operatore ::Classi ed Oggetti: l’operatore ::

Una volta definiti i prototipi delle funzioni membro, occorre scrivere il codice delle funzioni e far riconoscere al compilatore che la funzione è un metodo di una determinata classe. Per questo si usa l’operatore di risoluzione di ambito :: . Es: void contatore::reset ( ) { val = 0;};void contatore::incr ( ) { val++;};void contatore::decr ( ) { val--;};int contatore::get ( ) { return val;};

Classi ed Oggetti: l’operatore ::Classi ed Oggetti: l’operatore ::

Tramite l’operatore di risoluzione di ambito il compilatore riconosce di quale classe la funzione membro è un metodo e distingue tra metodi aventi lo stesso nome ma appartenenti a classi differenti.

Quando una funzione membro chiama una funzione che appartiene alla stessa classe, può farlo direttamente senza ricorrere all’operatore di risoluzione di ambito.

Classi ed Oggetti:dichiarazione di oggettiClassi ed Oggetti:dichiarazione di oggetti

Una volta definita una classe, è possibile istanziare un numero qualsiasi di oggetti appartenenti alla classe. Questi oggetti saranno distinti tra loro, avranno la stessa rappresentazione e potranno usare gli stessi metodi forniti dalla classe.Es:contatore c1, c2;definisce due oggetti c1 e c2 di tipo contatore ognuno dei quali ha la propria copia di val e può eseguire solo le operazioni definite nella classe contatore.

Classi ed Oggetti: accesso ai membriClassi ed Oggetti: accesso ai membri

Per usare i membri di un oggetto si usano l’operatore freccia e l’operatore punto con le stesse modalità usate per le struct. Es:c1.reset( );Questa istruzione ha il significato di invio di messaggio all’oggetto c1 di eseguire l’operazione reset( ) che azzera la variabile privata val.

Un oggetto può inoltre accedere al proprio indirizzo tramite un puntatore di nome this

Classi ed Oggetti: Costruttori e DistruttoriClassi ed Oggetti: Costruttori e DistruttoriNella maggior parte dei casi occorre inizializzare alcune parti dell’oggetto prima che siano utilizzate. Il C++ consente agli oggetti di inizializzarsi al momento della loro creazione tramite i costruttori.Un costruttore è una funzioni membro pubblica di una classe che ha le seguenti caratteristiche• ha lo stesso nome della classe di appartenenza • non deve avere un tipo di ritorno (neanche void)• può avere argomenti di default

Es:class counter {public:counter (int n=0) {val = n; };void reset( );void incr ();void decr ( );int get( );private:int val; };

Costruttore

Classi ed Oggetti: Costruttori e DistruttoriClassi ed Oggetti: Costruttori e Distruttori

Classi ed Oggetti: Costruttori e DistruttoriClassi ed Oggetti: Costruttori e DistruttoriI costruttori sono invocati automaticamente quando gli oggetti vengono istanziati.Es:contatore c1, c2(6);Quando viene eseguita l’istruzione sopra descritta, c1 viene creato con val = 0 (valore di default) mentre c2 viene istanziato con val = 6.

Il costruttore viene invocato quando viene allocata memoria all’oggetto:contatore *p; //il costruttore non è eseguitop = new contatore(2) ; //il costruttore è eseguito

Classi ed Oggetti: Costruttori e DistruttoriClassi ed Oggetti: Costruttori e DistruttoriSe un costruttore ammette argomenti non di default, questi devono essere specificati nella dichiarazione dell’oggetto.Es.class counter {public:counter (int n, int p) {val = n; sem = p; };…private:int val,sem};

counter c1(2,0), c2(6,3), c3(7,4);

Classi ed Oggetti: Costruttori e DistruttoriClassi ed Oggetti: Costruttori e DistruttoriIl distruttore è complementare al costruttore e viene usato ogni volta che un oggetto viene distrutto, cosa che avviene quando si esce dall’ambito dell’oggetto.Un distruttore è una funzioni membro pubblica di una classe che ha le seguenti caratteristiche• ha lo stesso nome della classe di appartenenza preceduto dal carattere ~. • non deve avere un tipo di ritorno (neanche void)• non può avere argomenti

Es:class counter {public:counter (int n=0) {val = n; };~counter ( ) {cout << “contatore distrutto\n”; };void reset( );void incr ();void decr ( );int get( );private:int val; };

Distruttore

Classi ed Oggetti: Costruttori e DistruttoriClassi ed Oggetti: Costruttori e Distruttori

Un esempio della necessità di distruttore si ha quando un oggetto viene rilasciato ma non tutta la memoria occupata da esso viene rilasciataEs:class stringa {char *str;public:stringa (char *str) {str = new char[80]; };set (char *str);show( );};

Quando viene rilasciato un oggetto di tipo stringa, l’area di memoria creata con new non è rilasciata e non è più disponibile sull’heap!!!!

Classi ed Oggetti: Costruttori e DistruttoriClassi ed Oggetti: Costruttori e Distruttori

Modificando la classe introducendo un distruttore si ha class stringa {char *str;public:stringa (char *str) {str = new char[80]; };~stringa( ) {if (str != NULL) delete[ ] str; }set (char *str);show( ); };

e si risolve il problema, poiché viene rilasciata l’area di memoria heap tramite la delete[ ].

Classi ed Oggetti: Costruttori e DistruttoriClassi ed Oggetti: Costruttori e Distruttori

• Un oggetto può essere raggruppato in arrayEs: contatore cont[4];• Si può accedere ad un oggetto tramite puntatore. Per un oggetto vale l’aritmetica dei puntatori, l’operatore &, l’operatore di dereferenza, l’operatore punto, l’operatore freccia •I reference a oggetti sono possibili come i reference a variabili• si possono passare degli oggetti a funzioni come qualsiasi altra variabile e le funzioni possono restituire un oggetto

Classi ed Oggetti: Costruttori e DistruttoriClassi ed Oggetti: Costruttori e Distruttori

Vi sono comunque dei problemi:1) quando un oggetto viene passato ad una funzione ne viene fatta una copia bit a bit che viene data al parametro della funzione. Se l’oggetto contiene un puntatore a una locazione di memoria allocata dinamicamente, allora la copia punterà alla medesima regione di memoria, ed ogni modifica si rifletterà sull’originale. L’oggetto originale ne viene pertanto influenzato.

Classi ed Oggetti: Costruttori e DistruttoriClassi ed Oggetti: Costruttori e Distruttori

Classi ed Oggetti: Costruttori e Distruttori Classi ed Oggetti: Costruttori e Distruttori

Oggetto stringain main()

Copia dell’oggettostringa nel passaggio a funzione

Area di memoria per str

2) quando un funzione restituisce un oggetto, il compilatore genera automaticamente un oggetto temporaneo contenente il valore restituito alla funzione. Quando il valore viene restituito alla funzione chiamante, l’oggetto temporaneo va fuori ambito e provoca la chiamata al distruttore, che potrebbe eliminare qualcosa di necessario alla routine chiamante, come della memoria allocata dinamicamente.

Classi ed Oggetti: Costruttori e DistruttoriClassi ed Oggetti: Costruttori e Distruttori

Classi ed Oggetti: Costruttori e Distruttori Classi ed Oggetti: Costruttori e Distruttori

Oggetto stringarestituito dallafunzione

Oggetto temporaneostringa contenente ilvalore dell’oggettostringa restituito dallafunzione e sottopostoall’azione del distruttore

Area di memoria per str

La risoluzione a questo tipo di problema è il costruttore di copia. Con il costruttore di copia, è possibile specificare con precisione cosa accade quando :• un oggetto viene usato per inizializzarne un altro in un’istruzione di dichiarazione• un oggetto viene passato come parametro a una funzione• viene creato un oggetto temporaneo da usare come valore restituito ad una funzione

Classi ed Oggetti: Costruttori e DistruttoriClassi ed Oggetti: Costruttori e Distruttori

La forma più comune per il prototipo del costruttore di copia è la seguente:nome_classe (const nome_classe &);Es:class stringa {char *str;public:stringa(char *, int =40); //costruttore normalestringa(const stringa &,int =40); //costruttore di copiachar *getcar( ){ return str;}};

Classi ed Oggetti: Costruttori e DistruttoriClassi ed Oggetti: Costruttori e Distruttori

stringa::stringa(char *inp, int SIZE=40){ int i; str = new char[SIZE]; cout << "sono qui \n"; for(i=0; *(inp+i) != '\0';i++) str[i]=inp[i]; str[i]= '\0';};

stringa::stringa(const stringa &ob,int SIZE=40){ int i;

str = new char[SIZE]; for(i=0;ob.str[i]!='\0';i++) str[i]=ob.str[i]; str[i]='\0';};

Classi ed Oggetti: Costruttori e DistruttoriClassi ed Oggetti: Costruttori e Distruttori

void display(stringa ob){ for(int i=0;*(ob.getcar()+i) != '\0';i++) cout << *(ob.getcar()+i); cout << "\n"; };main ( ){

stringa a("PIPPO25");display(a); //viene invocato il costruttore di copiareturn 0;

}

Classi ed Oggetti: Costruttori e DistruttoriClassi ed Oggetti: Costruttori e Distruttori

E’ possibile effettuare l’overload degli operatori del C++ in relazione ai tipi di classe. E’ possibile, per esempio, effettuare un overload dell’operatore + e fargli eseguire delle operazioni definite dal programmatore. Il concetto di overload è pertanto strettamente legato all’overloading delle funzioni. La forma generale di una funzione operatore utilizza la parola chiave operator nel seguente modotipo nome_classe::operator op(argomenti);dove op è l’operatore di cui si vuole effettuare l’overloading.

Classi ed Oggetti: Overload di operatoriClassi ed Oggetti: Overload di operatori

Es:class parall {int x,y,z;public:parall operator=(parall);…..};parall parall::operator=(parall ob){parall tmp; tmp.x = ob.x; tmp.y= ob.y; tmp z = ob.z; return tmp; }

Classi ed Oggetti: Overload di operatoriClassi ed Oggetti: Overload di operatori

Es:parall a, b;…..b=a; //viene usato l’operatore = di parall

L’operatore = effettua l’overloading dell’operatore = standard in C++. Anche altri operatori (p.e. &&, ||) standard in C++ possono essere ridefiniti.

Classi ed Oggetti: Overload di operatoriClassi ed Oggetti: Overload di operatori

La composizione in C++ avviene specificando gli oggetti componenti (oggetti membro) all’interno della classe di cui fanno parte. Occorre comunque indicare gli argomenti del costruttore della classe composta da passare ai costruttori degli oggetti membroQuesto avviene tramite gli inizializzatori di membro, che vengono usati nella definizione del costruttore.

Classi ed Oggetti: ComposizioneClassi ed Oggetti: Composizione

Es:class Data:{Data (int, int, int);…};Impiegato::Impiegato( char *nome, int giorno, int mese, int anno): datadinascita(int giorno, int mese, int anno){…};

Se una classe Impiegato ha un oggetto membro datadinascita di classe Data, i parametri giorno, mese e anno saranno passati al costruttore Data

Classi ed Oggetti: ComposizioneClassi ed Oggetti: Composizione

Nella terminologia del C++, la superclasse si chiama classe base, mentre la sottoclasse si chiama classe derivata. Le modalità di derivazione di una classe da una classe base sono specificate tramite lo specificatore di accesso alla classe base e la forma generale per dichiarare che una classe eredita da un’altra è: class classe_derivata: accesso classe base {corpo della classe_derivata}

dove accesso può essere private, public e protected

Classi ed Oggetti: EreditarietàClassi ed Oggetti: Ereditarietà

Es:class impiegato {public:Impiegato (char *) ;~Impiegato( );void print( ) const;private:char *nome;};class interinale: public impiegato {public:…..void print( ) const;private:double orario;double salario;};

Classi ed Oggetti: EreditarietàClassi ed Oggetti: Ereditarietà

• Quando una classe base viene ereditata come pubblica, tutti suoi membri pubblici diventano pubblici per la classe derivata mentre i suoi membri privati sono nascosti alla classe derivata.

• Quando una classe base viene ereditata come privata, tutti i suoi membri pubblici diventano membri privati della classe derivata mentre i suoi membri privati sono nascosti alla classe derivata

Classi ed Oggetti: EreditarietàClassi ed Oggetti: Ereditarietà

Ricordiamo che i membri di una classe possono essere private, public e protected. La modalità protected è simile alla private , con una differenza che si riscontra nel caso di classe derivata: quando una classe base viene ereditata come pubblica, tutti suoi membri protected diventano membri protetti per la classe derivata;quando una classe base viene ereditata come privata, tutti suoi membri protected diventano membri privati per la classe derivataIn questo modo è possibile dar vita a membri che sono privati per la loro classe ma che sono ereditabili ed accessibili per una classe derivata.

Classi ed Oggetti: EreditarietàClassi ed Oggetti: Ereditarietà

Es:class base {public:void set(int) ;...protected:int i, j; // privato per base, ma accessibile per derivato};class derivato: public base {public:… // può accedere a i e j di baseprivate:int k;};

Classi ed Oggetti: EreditarietàClassi ed Oggetti: Ereditarietà

Quando una classe base viene ereditata come protected, tutti suoi membri pubblici e protetti diventano protetti per la classe derivata mentre i suoi membri privati sono nascosti alla classe derivata.

Classi ed Oggetti: EreditarietàClassi ed Oggetti: Ereditarietà

Una classe derivata eredita i membri dalla classe base e pertanto , quando si istanzia un oggetto di classe derivata, il C++ chiama dapprima il costruttore della classe base e successivamente il costruttore della classe derivata. L’esecuzione dei distruttori avviene in ordine inverso: dapprima vengono eseguiti i distruttori della classe derivata e successivamente quelli della classe base.

Classi ed Oggetti: EreditarietàClassi ed Oggetti: Ereditarietà

Nel caso in cui i costruttori della classe base necessitino di parametri, nella dichiarazione dei costruttori delle classi derivate si utilizzano gli inizializzatori di membro :Es:Interinale::Interinale (char *nome):impiegato(nome){….}

Classi ed Oggetti: EreditarietàClassi ed Oggetti: Ereditarietà

E’ possibile che una classe erediti le proprietà di più classe basi. In questo caso, si ha la eredità multipla (multiple inheritance).In questo caso la forma generale per dichiarare una classe con multiple inheritance è class classe_derivata: accesso1 classe_base1, accesso2 classe_base2,..accesson classe_basen {corpo della classe_derivata}

dove accesso1,..,accesson possono essere private, public e protected

Classi ed Oggetti: EreditarietàClassi ed Oggetti: Ereditarietà

Sono possibili alcune ambiguità nei casi di eredità multipla:class a {public: int i};class b: public a {public: int j};class c: public a {public: int k};class d: public b, public c { int sum};d ob;ob.i = 5; // istruzione ambigua: quale i ??

A quale i si riferisce? A quella di b o c ?? Si introduce un elemento di ambiguità.

Classi ed Oggetti: EreditarietàClassi ed Oggetti: Ereditarietà

Per risolvere questi tipo di ambiguità , si usano le classi base virtuali.La loro semantica è simile alle classi base non virtuali, con in aggiunta la parola chiave virtual prima delle definizione di accesso.Es:class a {public: int i};class b: virtual public a {public: int j};

In questo modo, quando due o più oggetti sono derivati da una classe base comune, è possibile evitare che in un oggetto derivato da quegli oggetti siano presenti più copie della classe base.

Classi ed Oggetti: EreditarietàClassi ed Oggetti: Ereditarietà

Il polimorfismo viene implementato tramite le funzioni virtuali.

Una funzione virtuale si intende una funzione che viene preceduta dalla parola chiave virtual in una classe base e poi ridefinita in una o più classi derivate.

Classi ed Oggetti: PolimorfismoClassi ed Oggetti: Polimorfismo

Es:class a {.. public: virtual void print ( ) {cout << “prima stampa”;} };class b: public a {.. public: void print ( ) {cout << “seconda stampa”;} };

Classi ed Oggetti: PolimorfismoClassi ed Oggetti: Polimorfismo

Ciascuna classe derivata può quindi avere la propria versione di funzione virtuale, pur mantenendo la medesima interfaccia.

Quando una classe derivata non ridefinisce una funzione virtuale, la funzione viene impiegata nel modo in cui è definita nella classe base

Classi ed Oggetti: PolimorfismoClassi ed Oggetti: Polimorfismo