C++ - Lezione III
Programmazione Orientata agli Oggetti
Classi ed Oggetti
Operatori
1
Paradigma di Programmazione
Le due componenti principali dei programmi:


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


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
software molto complessi
Un progetto software complesso deve evitare

Rigidità e Fragilità
 non può essere cambiato con faciltà
 non può essere stimato l’impatto di una modifica
 una modifica singola causa una cascata di modifiche
successive

Non riusabilità
 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
Classi ed Oggetti
Cosa è un oggetto?



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 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 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 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 chi 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
client
8
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

Definizione di nuovi tipi (oltre a int, float, double) come:
 numeri complessi,
 vettori,
 matrici
 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
public:
Complex();
costruttori
Complex(double init_real, double init_im);
void setReal(double);
void setIm(double);
double GetReal();
double GetIm();
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 della classe a
cui appartiene


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

viene normalmente utilizzato:
 quando un oggetto è inizializzato per assegnazione
 quando un oggetto è passato come argomento ad una funzione
 quando un oggetto è ritornato da una funzione

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
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 è 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 init_real,double init_im {
_real = init_real;
_im=init_im;
}
void Complex::setReal(double x){
_real=x;
}
void Complex::setIm(double y){
_im=y;
}
double Complex::GetReal(){
return _real;
}
double Complex::GetIm(){
return _im;
}
useComplex.cpp
#include <iostream.h>
#include “Complex.h”
using namespace std;
invoca il construttore
int main(){
Complex c(1, 1);
cout << “ c = “
<< c.GetReal() << “+i”
<< c.GetIm() << endl;
c.setIm(-1);
cout << “ c = “
<< c.GetReal() << “+i”
<< c.GetIm() << endl;
return 0;
}
si accede ai metodi ed
agli attributi public
attraverso l'operatore .
15
Uso dei Puntatori
useComplex.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 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è ne possiede solo un puntatore

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 {
class B {
....
private:
private:
A * a;
A * a;
};
Dipendenze cicliche
....
};
class B;
class A;
class A {
class B {
....
....
private:
private:
B * b;
A * a;
};
};
18
Public & Private
Dalle applicazioni client si puo' accedere solo ai metodi o
attributi dichiarati public useComplex.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 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;
}
useComplex.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);
print(&c)
return 0;
Eseguiamo ed interpretiamo l'output20
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;
}
$ ./useComplex.exe
c = 1, 1
chiamata del distruttore
c = 1, 1
chiamata del distruttore
chiamata del distruttore
21
Oggetti Costanti
E' regola di buona programmazione dichiarare
costante un oggetto che non deve venire modificato
usando la parola chiave const

const Complex c(1,1);
In questo modo il compilatore limita l'accesso ai
metodi dell'oggetto

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

double getreal() const;
22
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 setReal(double);
double setIm(double);
double getReal() const;
modificatori
selettori o metodi di accesso
double getIm() 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 di quella classe
usaContatore.cpp
Class Particella {
public:
Particella(){++count;}
~Particella(){--count;}
int Contatore::count=0;
Int
Particella::count=0;
static int count;
int main(){
Contatored1,
Particella
d1,d2;
d2;
cout << "Adesso è " << Contatore::count
d1.count << endl;<< endl;
{
Contatore d3, d4, d5;
cout << "Adesso è " << Contatore::count
d1.count << endl;<< endl;
}
cout << "Adesso è " << Contatore::count
d1.count << endl;<< endl;
Contatore d6;
cout << "Adesso è " << Contatore::count
d1.count << endl;<< endl;
Private:
Double _mass;
OUTPUT...
Int _charge;
$ ./usaContatore.exe
Double _px;
};
#include <iostream.h>
#include “Particella.h”
“Contatore.h”
using namespace std;
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 static non possono accedere ad attributi non statici della
classe
usaContatore.cpp
useDummy.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 getCount(){return count;}
private:
static int count;
};
int main(){
Contatore d1, d2;
cout << "Adesso esistono " << Contatore.getCount()
d1.count << " dummies
<<" " <<
dummies
endl;
"{ << endl;
{ Contatore d3, d4, d5;
cout
Contatore
<< "Adesso
d3, d4,esistono
d5;
" << d1.count << " dummies " << endl;
}cout << "Adesso esistono " << Contatore.getCount() << " dummies
" cout
<< endl;
<< "Adesso esistono " << d1.count << " dummies " << endl;
}Contatore d6;
....
cout << "Adesso esistono " << d1.count << " dummies " << endl;
return 0;
}
25
}
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

Cosa manca per modellare il problema "numero complesso"?
Progettare e usare la classe Vector3D
#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

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 Implementazione
29
Stack di Interi
Content
val
Stack
next
top
Content
...
Content
val
val
next
next
class Content {
public:
Content( int, Content* );
int getVal ();
Content * getNext();
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
Scarica

C++_lez3_mod