C Lezione III Programmazione Orientata agli Oggetti Classi

  • Slides: 30
Download presentation
C++ - Lezione III Programmazione Orientata agli Oggetti Classi ed Oggetti Operatori 1

C++ - Lezione III Programmazione Orientata agli Oggetti Classi ed Oggetti Operatori 1

Paradigma di Programmazione Le due componenti principali dei programmi: n n Algoritmi: l’insieme delle

Paradigma di Programmazione Le due componenti principali dei programmi: n n Algoritmi: l’insieme delle istruzioni che svolgono un particolare compito Dati: ciò su cui gli algoritmi agiscono per produrre una soluzione unica La relazione fra queste componenti definisce il paradigma di programmazione n n Programmazione procedurale: problemi modellati dagli algoritmi. Dati immagazzinati in aree comuni o passate agli algoritmi Programmazione ad oggetti: problemi modellati dalle relazioni fra tipi di dati astratti (ADT, Abstract Data Types), chiamati generalmente oggetti 2

Programmazione per Oggetti Programmare per oggetti facilita la progettazione e il mantenimento di sistemi

Programmazione per Oggetti Programmare per oggetti facilita la progettazione e il mantenimento di sistemi software molto complessi Un progetto software complesso deve evitare n Rigidità e Fragilità w non può essere cambiato con faciltà w non può essere stimato l’impatto di una modifica w una modifica singola causa una cascata di modifiche successive n Non riusabilità w esistono molte interdipendenze, quindi non è possibile estrarre parti che potrebbero essere comuni 3

Concetti della Programmazione Orientata agli Oggetti Classi ed oggetti Incapsulamento Ereditarietà Polimorfismo 4

Concetti della Programmazione Orientata agli Oggetti Classi ed oggetti Incapsulamento Ereditarietà Polimorfismo 4

Classi ed Oggetti Cosa è un oggetto? n n n Un oggetto è un’entità

Classi ed Oggetti Cosa è un oggetto? n n n Un oggetto è un’entità che si possa immaginare dotata di determinate caratteristiche e funzionalità Lo stato di un oggetto è rappresentato da dati che ne descrivono le caratteristiche Le funzionalità di un oggetto sono le operazioni che può svolgere quando glie lo si richiede (cioè quando riceve un messaggio) La classe è un astrazione concettuale dell'oggetto e ne definisce l'interfaccia (ovvero un tipo) 5

Esempio Classi oggetti elefante Elefante Catapulta Soldato. . . oggetti catapulta oggetti soldato Una

Esempio Classi oggetti elefante Elefante Catapulta Soldato. . . oggetti catapulta oggetti soldato Una classe rappresenta un tipo. L'oggetto è un istanza di quel tipo 6

Esempio (cont) Il codice client si riduce ad in insieme di messaggi (che costituisco

Esempio (cont) Il codice client si riduce ad in insieme di messaggi (che costituisco l'interfaccia delle classi) inviati agli oggetti Classe Soldato attacca() muoviti() difendi() arrenditi(). . . Codice client (main) Soldato s; //oggetto soldato s. attacca() s. muoviti(). . . 7

Incapsulamento Netta divisione fra interfaccia e implementazione Da fuori si vede solo l’interfaccia che

Incapsulamento Netta divisione fra interfaccia e implementazione Da fuori si vede solo l’interfaccia che definisce i messaggi accettati dall’oggetto I dettagli dell’implementazione (dati e codice delle funzioni) sono invisibili dall’esterno Ogni oggetto ha in se tutto ciò che gli serve per rispondere alle chiamate (o deve sapere a chiedere…) Il confinamento di informazioni e funzionalità in oggetti permette livelli maggiori di astrazione e semplifica la gestione di sistemi complessi. Implementazione Interfaccia Classe client usa client 8

Vantaggi Riduzione della dipendenza del codice di alto livello dalla rappresentazione dei dati Riutilizzo

Vantaggi Riduzione della dipendenza del codice di alto livello dalla rappresentazione dei dati Riutilizzo del codice Sviluppo moduli indipendenti l’uno dall’altro Il codice utente (client) dipende dalle interfacce ma non dall’implementazione 9

Classi C++ Object Orientation è implementata in C++ attraverso il concetto di classe n

Classi C++ Object Orientation è implementata in C++ attraverso il concetto di classe n Definizione di nuovi tipi (oltre a int, float, double) come: w numeri complessi, w vettori, w matrici w forme geometriche. . . Le classi permettono di modellare una problema che rappresenti la realtà Una classe C++ è formata da I dati (o attributi) di una classe definiscono lo stato dell’oggetto Le funzioni (o metodi) di una classe implementano la risposta ai messaggi 10

Esempio : Classe Numero Complesso class Complex { Definito nell' hedear file Complex. h

Esempio : Classe Numero Complesso class Complex { Definito nell' hedear file Complex. h public: Complex(); costruttori Complex(double init_real, double init_im); void set. Real(double); void set. Im(double); double Get. Real(); double Get. Im(); metodi Gli attributi privati non sono accessibili al di fuori della classe I metodi pubblici sono gli unici visibili private: double _real; attributi o dati membro double _im; }; Punto e virgola Definizione/Interfaccia della classe numero complesso 11

Classi C++ - Costruttori Un costruttore è un metodo il cui nome è quello

Classi C++ - Costruttori Un costruttore è un metodo il cui nome è quello della classe a cui appartiene n n costruisce gli oggetti della classe. Questo implica l’inizializzazione degli attributi e, frequentemente, allocazione di memoria dinamica Un costruttore la cui lista di argomenti è vuota o composta di argomenti di default viene normalmente chiamato costruttore di default Il costruttore di copia è costruttore che ha come argomento un riferimento ad un oggetto della stessa classe n viene normalmente utilizzato: w quando un oggetto è inizializzato per assegnazione w quando un oggetto è passato come argomento ad una funzione w quando un oggetto è ritornato da una funzione n Se non viene fornito esplicitamente dall’utente, il compilatore ne genererà uno automaticamente Complex(const Complex& v) 12

Ancora Costruttori La maggior parte dei costruttori serve ad inizializzare la lista dei dati

Ancora Costruttori La maggior parte dei costruttori serve ad inizializzare la lista dei dati membro di un oggetto Questo puo' essere fatto anche attraverso la lista di inizializzazione Possono essere usati valori di default Il costruttore di copia, copia lo stato di uno oggetto in un altro oggetto Complex: : Complex(double init_real, double init_im) { _real = init_real; _im=init_im; } Complex: : Complex(double init_real, double init_im ): _real(init_real), _im(init_im) {} Complex: : Complex(double init_real=0, double init_im=0 ): real(init_real), im(init_im) {} Complex: : Complex(const Complex& c) : _real(c. real), _im(c. im) {} 13

Classi C++ - Il Distruttore Il distruttore è un metodo il cui nome è

Classi C++ - Il Distruttore Il distruttore è un metodo il cui nome è quello della classe a cui appartiene preceduto da una tilde (~) Il distruttore viene chiamato automaticamente quando un oggetto sta per essere distrutto (sia perchè delete è stato invocato sia perchè l’oggetto è finito fuori scope) Il compito del distruttore è di assicurarsi che l’oggetto per cui è invocato verrà distrutto senza conseguenze. In particolare, se memoria è stata allocata nel costruttore, il distruttore dovrà assicurarsi di liberarla Complex: : ~Complex() 14

Complex. cc #include “Complex. h” Complex: : Complex(){ _real=0; _im=0; } Complex: : Complex(double

Complex. cc #include “Complex. h” Complex: : Complex(){ _real=0; _im=0; } Complex: : Complex(double init_real, double init_im { _real = init_real; _im=init_im; } void Complex: : set. Real(double x){ _real=x; } void Complex: : set. Im(double y){ _im=y; } double Complex: : Get. Real(){ return _real; } double Complex: : Get. Im(){ return _im; } use. Complex. cpp #include <iostream. h> #include “Complex. h” using namespace std; invoca il construttore int main(){ Complex c(1, 1); cout << “ c = “ << c. Get. Real() << “+i” << c. Get. Im() << endl; c. set. Im(-1); cout << “ c = “ << c. Get. Real() << “+i” << c. Get. Im() << endl; return 0; } si accede ai metodi ed agli attributi public attraverso l'operatore. 15

Uso dei Puntatori use. Complex. cpp #include <iostream. h> #include “Complex. h” using namespace

Uso dei Puntatori use. Complex. cpp #include <iostream. h> #include “Complex. h” using namespace std; Allocazione dinamica int main(){ Complex * c = new Complex(1, 1); cout << “ c = “ << c->real() << “+i” << c->im() << endl; } delete c; return 0; 16

Uso della Definizione di una Classe In generale un header file con la definizione

Uso della Definizione di una Classe In generale un header file con la definizione di una classe puo' essere incluso (#include) in piu' di un sorgente Il file verra` effettivamente compilato piu' volte e ci saranno piu' volte le stesse dichiarazioni (e definizioni) che daranno luogo a errori di definizione ripetuta dello stesso oggetto. Si puo' ovviare al problema utilizzando la direttiva di preprocessore #ifndef COMPLEX_H #define COMPLEX_H /* contenuto file header */ #endif si verifica cioe` se un certo simbolo e` stato definito, se non lo e` (cioe` #ifndef e` verificata) si definisce il simbolo e poi si inserisce il codice C/C++, alla fine si inserisce l'#endif. 17

Forward Declaration B non ha bisogno di conoscere i dettagli della classe A poichè

Forward Declaration B non ha bisogno di conoscere i dettagli della classe A poichè ne possiede solo un puntatore n Nel file sorgente bisogna includere la classe con #include altrimenti non si puo' fare nessuna operazione con A Velocizza la compilazione class A {. . . }; #include "A. h" class A; class B { . . private: A * a; }; Dipendenze cicliche . . }; class B; class A { class B { . . . . private: B * b; A * a; }; 18

Public & Private Dalle applicazioni client si puo' accedere solo ai metodi o attributi

Public & Private Dalle applicazioni client si puo' accedere solo ai metodi o attributi dichiarati public use. Complex. cpp #include <iostream. h> #include “Complex. h” using namespace std; int main(){ Complex v(1, 1); cout << “ c = “ << c. _real << “, ” << c. _im << endl; //ERRORE!!!!!! return 0; } Generalmente gli attributi di una classe vengono dichiarati private mentre i metodi sono public. Tuttavia questa dicotomia non è obbligatoria: in alcuni casi puo' essere utile dichiarare un metodo private per funzioni di utilità locale 19

Esercizio: Tracciamo i Costruttori e Distruttori 1. 2. implemetiamo la classe Complex aggiungiamo dei

Esercizio: Tracciamo i Costruttori e Distruttori 1. 2. implemetiamo la classe Complex aggiungiamo dei print-out ai costruttori (e distruttori) del tipo : Complex: : Complex(const Complex& c) : real(c. real), im(c. im){ cout<<"chiamata al costruttore di copia" << endl; } use. Complex. cpp #include <iostream. h> #include “Complex. h” using namespace std; void print(Complex c) {cout << “ c = “ << c. real() << “, ” << c. im() << endl; } void print(Complex * c) {cout << “ c = “ << c->real() << “, ” << c->im() << endl; } int main(){ } Complex c(1, 1); Complex v(c); print(&c) return 0; Eseguiamo ed interpretiamo l'output 20

Output. . . void print(Complex p) {cout << “ c = “ << c.

Output. . . void print(Complex p) {cout << “ c = “ << c. real() << “+i” << c. im() << endl; } void print(Complex * p) {cout << “ c = “ << c ->real() << “+i” << c->im() << endl; } int main(){ chiamata del costruttore Complex c(1, 1); chiamata del costruttore di copia Complex v(c); chiamata del costruttore di copia print(c); print(&c) return 0; } $. /use. Complex. exe c = 1, 1 chiamata del distruttore 21

Oggetti Costanti E' regola di buona programmazione dichiarare costante un oggetto che non deve

Oggetti Costanti E' regola di buona programmazione dichiarare costante un oggetto che non deve venire modificato usando la parola chiave const n const Complex c(1, 1); In questo modo il compilatore limita l'accesso ai metodi dell'oggetto n c. getreal() // errore!! I soli metodi che potrebbero essere chiamate per oggetti const sarebbero il costruttore ed il distruttore Per superare questa restrizione dobbiamo dichiarare come constanti quei metodi che vogliamo poter usare su oggetti const n double getreal() const; 22

Metodi "Set" e "Get" Metodi di tipo get: metodo che non modifica lo stato

Metodi "Set" e "Get" Metodi di tipo get: metodo che non modifica lo stato (attributi) della classe. E’ dichiarato const Metodi di tipo set: metodo che può modificare lo stato della classe class Complex { public: Complex(); Complex(double init_real, double init_im); double set. Real(double); double set. Im(double); double get. Real() const; modificatori selettori o metodi di accesso double get. Im() const; private: double real; l'uso di const permette ai metodi di operare su oggetti constanti double im; }; 23

Attributi static Attributi dichiarati static in una classe sono condivisi da tutti gli oggetti

Attributi static Attributi dichiarati static in una classe sono condivisi da tutti gli oggetti di quella classe Class Particella { public: Particella(){++count; } #include <iostream. h> #include “Particella. h” “Contatore. h” using namespace std; ~Particella(){--count; } int Contatore: : count=0; Int Particella: : count=0; static int count; int main(){ Contatored 1, Particella d 1, d 2; cout << "Adesso è " << Contatore: : count d 1. count << endl; { Contatore d 3, d 4, d 5; cout << "Adesso è " << Contatore: : count d 1. count << endl; } cout << "Adesso è " << Contatore: : count d 1. count << endl; Contatore d 6; cout << "Adesso è " << Contatore: : count d 1. count << endl; Private: Double _mass; OUTPUT. . . Int _charge; $. /usa. Contatore. exe Double _px; }; usa. Contatore. cpp Adesso e' 2 Adesso e' 5 Adesso e' 2 Adesso e' 3 } 24

Metodi static Funzione definita in una classe indipendende dagli oggetti della classe Metodi dichiarati

Metodi static Funzione definita in una classe indipendende dagli oggetti della classe Metodi dichiarati static non possono accedere ad attributi non statici della classe usa. Contatore. cpp use. Dummy. cpp Attiributi statici possono #include <iostream. h> essere usati e modificati #include “Contatore. h” using namespace std; soltanto da metodi statici int Contatore: : count=0; class Contatore { public: Contatore(){++count}; ~Contatore(){--count}; static int get. Count(){return count; } private: static int count; }; int main(){ Contatore d 1, d 2; cout << "Adesso esistono " << Contatore. get. Count() d 1. count << " dummies <<" " << dummies endl; "{ << endl; { Contatore d 3, d 4, d 5; cout Contatore << "Adesso d 3, d 4, esistono d 5; " << d 1. count << " dummies " << endl; }cout << "Adesso esistono " << Contatore. get. Count() << " dummies " cout << endl; << "Adesso esistono " << d 1. count << " dummies " << endl; }Contatore d 6; . . cout << "Adesso esistono " << d 1. count << " dummies " << endl; return 0; } 25 }

this La keyword this rappresenta un puntatore costante all'oggetto corrente Complex: : Complex(double init_real,

this La keyword this rappresenta un puntatore costante all'oggetto corrente Complex: : Complex(double init_real, double init_im) { this->_real = init_real; this->_im=init_im; } 26

Esercizi. . . Completare la classe Complex n Cosa manca per modellare il problema

Esercizi. . . Completare la classe Complex n Cosa manca per modellare il problema "numero complesso"? Progettare e usare la classe Vector 3 D #include <iostream> #include <time. h> Progettare e usare la classe Timer (Cronometro) using namespace std; int main(){ cout << time(0) << endl; time_t rawtime Utilizzare la funzione time(int) della libreria std time. h n time(&rawtime); cout << ctime(&rawtime) << endl; return 0; } $. /time. exe 1117146427 27 Fri May 27 16: 27: 07 2005

Numero Complesso - Definizione 28

Numero Complesso - Definizione 28

Numero Complesso Implementazione 29

Numero Complesso Implementazione 29

Stack di Interi Content val Stack next top Content . . . Content val

Stack di Interi Content val Stack next top Content . . . Content val next class Content { public: Content( int, Content* ); int get. Val (); Content * get. Next(); private: Content * next; int val; }; Stack top Lo stack vuoto push pop class Stack { public: Stack(); ~Stack(); void push(int); int pop (); private: Content * top; }; 30