Introduzione
alla programmazione ad oggetti in C++
Object Oriented Programming, OOP
E.Mumolo. DEEI
[email protected]
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
2
Struttura di un programma
•
Spaghetti code
•
Programmazione strutturata
3
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)
4
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
#include <iostream.h>
//oop1.cpp
main()
{
const int i=1;
const int *pi=&i;
const double pg=3.1415926;
char str[]="giorgio";
char str1[]="luigi";
const char *n;
//n e' un puntatore ad una stringa costante
n=str; n=str1;
//n puo' essere cambiato
cout << n << '\n';
//
*n="antonio"; ILLEGALE: il dato puntato da n e' costante e non puo' essere cambiato
char *const a = str;
//a e' un puntatore costante a char
//a deve essere inizializzato e non puo' essere cambiato
//
a=str1; ILLEGALE: il puntatore non puo' essere cambiato
cout << "i=" << i << " *pi=" << *pi << " pg*i=" << pg**pi << " nome=" << a <<'\n';
const char const*c = str1; //sia il puntatore che il valore puntato sono costanti!!
cout << c << '\n';
}
Output del programma
luigi
i=1 *pi=1 pg*i=3.14159
luigi
nome=giorgio
5
•
Dichiarazioni variabili: non solo all’inizio del programma, ma anche nei cicli
 for(int i=0; i<N; i++) {...}
•
Definizione di struttura, enum, unione
//oop2.cpp
#include <iostream.h>
enum logico { falso, vero };
struct valore
{
int dato;
float somma;
logico var;
union
// unione senza nome: lo spazio occupato e' quello dell'elemento piu' grande!
{
float val;
char a[10];
logico p;
};
};
main()
{
valore i, j, k[500];
// non serve struct valore!!
logico a, b, c[50];
// non serve enum!!
i.dato=10; j.somma=20; i.var=vero; i.val=3.14; strcpy(j.a,"PROVA!!");
a=vero; b=falso;
cout<<"i.dato="<<i.dato<<" j.somma="<<j.somma<<'\n'<<" stringa="<<j.a<<'\n';
if(a==vero) cout << " logico a=vero\n"; else cout << " logico a=falso\n" ;
if(b==vero) cout << " logico b=vero\n"; else cout << " logico b=falso\n" ;
}
Output:
i.dato=10 j.somma=20
stringa=PROVA!!
logico a=vero
logico b=falso
6
•
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
//oop3.cpp
#include <iostream.h>
main()
{
float p;
float &rp=p;
//rp=riferimento a p (alias di p)
p=3.14;
rp=6.28;
cout << "p =" << p << " rp=" << rp << '\n';
//
const float &rp1=p; //rp1 e' alias di p, ma l'oggetto riferito e' costante
//rp1 non puo' modificare l'oggetto
rp1=3.1; ILLEGALE: non si puo' modificare un oggetto costante!
cout << "rp1=" << rp1 <<'\n';
}
Output:
p =6.28 rp=6.28
rp1=6.28
7
•
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
8
//Altri esempi di alias
#include <iostream.h>
//oop5.cpp
void scambia(int &rx, int &ry)
{
int t=rx;
rx=ry; ry=t;
}
main()
{
int x=20; int y=30;
cout << "x, y =" << x << ' ' << y;
scambia(x,y);
cout << "x, y =" << x << ' ' << y;
}
//oop6.cpp *************************************************************
#include <iostream.h>
int &max(int &a, int &b)
{
if(a>=b) return a;
return b;
}
main()
{
int x, y;
cin >> x >> y;
cout << " il massimo tra " << x << " e " << y << "e' " << max(x,y);
}
Altra versione:
int &max(const int &a, const int &b)
{
if(a>=b) return (int &)a;
//il casting e’ necessario per via di const
return (int &)b;
}
9
•
argomenti di default in una funzione
//opp7.cpp
#include <iostream.h>
void build(float *val, int size=10)
{
for(short i=0; i<size; i++) val[i]=(float)i;
}
main()
{
float A[100];
int N=10;
build(A); //non e' necessario usare build(A,N): viene usato il valore di default
for(short i=0; i<10; i++) cout << A[i] << ' ';
}
•
•
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
10
•
•
•
•
•
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
//oop8.cpp
#include <iostream.h>
int a=1;
main()
{
int aext=a; int a=2;
{
int aext=a; int a=3;
{
int aext=a; int a=4;
cout << "exta=" << aext << " interno a=" << a << " ::a=" << ::a << endl;
}
cout << "aext=" << aext << " esterno a=" << a << " ::a=" << ::a << endl;
}
cout << "aext=" << aext << " main
a=" << a << " ::a=" << ::a << endl;
}
Output:
aext=3 interno a=4 ::a=1
aext=2 esterno a=3 ::a=1
aext=1 main
a=2 ::a=1
11
•
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 []
//oop9.cpp
#include <iostream.h>
main()
{
int *dati;
int dim;
cout << "dimensione array?" ;
cin >> dim;
dati= new int[dim];
for(short i=0; i<dim; i++) dati[i]=i;
for(short i=0; i<dim; i++) cout << dati[i] << ' ';
delete []dati;
}
//oop10.cpp
#include <iostream.h>
main()
{
int dim; char *nome;
cout << "quanti caratteri ha il nome?";
cin >> dim;
nome=new char[dim+1];
cout << endl << "scrivi il nome ";
cin >> nome;
cout << "hai scritto " << nome;
delete nome;
}
quanti caratteri ha il nome?7
scrivi il nome Antonio
hai scritto Antonio
12
//oop11.cpp
// esempi di riepilogo: gestione di liste semplici
#include <iostream.h>
struct nodo
{
int dato;
nodo *next;
};
enum booleano { falso, vero };
nodo *crea(int n)
{
nodo *p, *p0=0;
for(int i=1; i<=n; i++)
{
p=new nodo;
cout << "scrivi un numero "; cin>> p->dato;
p->next=p0;
p0=p;
}
return p0;
}
void insert(nodo *&p0, int a)
{
nodo *p=new nodo;
p->dato=a; p->next=p0;
p0=p;
}
booleano estraitesta(nodo *&p0, int &a)
{
nodo *p=p0;
if(p0==0) return falso;
a=p0->dato; p0=p0->next;
delete p;
return vero;
}
13
void accoda(nodo *&p0, int a)
{
nodo *p, *q;
for(q=p0;q!=0;q=q->next) p=q;
q=new nodo;
q->dato=a;
q->next=0;
if(p0==0) p0=q;
else p->next=q;
}
booleano estraicoda(nodo *&p0, int &a)
{
nodo *p=0, *q;
if(p0==0) return falso;
for(q=p0;q->next != 0; q=q->next) p=0;
q=q->next; if(q==p0) p0=0; else p->next=0;
delete q;
return vero;
}
main()
{
int a; char c;
nodo *r, *p;
p=crea(3);
for(int i=1; i<3; i++)
{
cout << " scrivi un numero "; cin >> a;
insert(p,a);
accoda(p,a);
}
while(estraitesta(p,a)==vero) cout << "valore=" << a << endl;
}
14
//oop12.cpp
//Riepilogando: gestione di alberi binari
#include <iostream.h>
struct nodo
{
int dato;
nodo *sx;
nodo *dx;
};
nodo * inserisci(nodo *r, int d)
{
if(r==0)
{
r=new nodo;
r->dato=d; r->sx=0;
r->dx=0;
}
else if(d<r->dato)
r->sx=inserisci(r->sx,d);
else if(d>r->dato)
r->dx=inserisci(r->dx,d);
return r;
}
void simmetrico(nodo *r)
{
if(r!=0)
{
simmetrico(r->sx); cout << r->dato;
simmetrico(r->dx);
}
}
void anticipato(nodo *r)
{
if(r!=0)
{
cout << r->dato;
anticipato(r->sx); anticipato(r->dx);
}
}
15
void differito(nodo *r)
{
if(r!=0)
{
differito(r->sx);
differito(r->dx);
cout << r->dato;
}
}
main()
{
nodo *rad, *p; int a;
for(short i=0; i<10; i++){
cout << "dare un numero " ; cin >> a;
p=inserisci(p,a);
}
cout << endl << "visita in ordine simmetrico" << endl;
simmetrico(p);
cout << endl << "visita in ordine anticipato" << endl;
anticipato(p);
cout << endl << "visita in ordine differito" << endl;
differito(p);
}
Output
4 3 2 1 8
visita in
123456789
visita in
432186579
visita in
123576984
6 9 5 3 7
ordine simmetrico
ordine anticipato
ordine differito
16
//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;
17
//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
}
}
18
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
}
19
•
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
//oop14.cpp
#include <iostream.h>
float media(float *a, int size=10)
{
float somma=0;
for(short i=0; i<size; i++) somma+=a[i];
return somma/size;
}
float media(int *a, int size=10)
{
int somma=0;
for(short i=0; i<size; i++) somma+=a[i];
somma=somma/size;
return (float)somma;
}
main()
{
int N=10; int a; int buf[10]; float buff[10];
for(short i=0; i<N; i++){
cout << endl << "scrivi un numero intero "; cin >> a;
buf[i]=a;
buff[i]=a;
}
cout << "media float=" << media(buff) << endl;
cout << "media int=" << media(buf) << endl;
}
20
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
21
•
•
•
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
22
•
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
23
sistema tramite classi.
• Top-down:
esempio
Automobile
Classe base
(componente complesso)
ruota
carrozzeria
motore
Sottoclassi (componenti minime)
• Bottom-up:
Classe base
(componente minimo)
veicolo
Veicolo
a motore
Veicolo
senza motore
moto
Sottoclassi (componenti via via piu’ complesse)
auto
taxi
aereo
24
Classi
•
Una classe (ADT):
class casa
{ ... };
•
•
•
Campo variabili
Metodo1
Metodo2
Metodo3
privato
pubblico
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;
var. a
var. mia
Puntatore ‘this’
var. tua
Puntatore ‘this’
Codice dei metodi
•
mia.metodo1; //attiva metodo1 mediante l’invio dell’indirizzo di mia al codice di
•
metodo1
Comunicazione tra oggetti tramite invio di messaggi di attivazione
25
Comunicazione tra oggetti
Campo variabili
Metodo1
Metodo2
Metodo3
Chiama tua.metodo2
a
Campo variabili
Metodo1
Metodo2
Metodo3
Campo variabili
Metodo1
Metodo2
Metodo3
tua
Chiama mia.metodo1
mia
26
In conclusione
•
Il mondo visto come sistema ad oggetti:
societa’ di entita’ attive, cooperanti, riutilizzabili
•
Progettazione bottom-up
•
Ereditarieta’, polimorfismo
•
Visione globale
27
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 28
•
•
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
29
//oop15.cpp
//scrittura dei metodi in linea
#include <iostream.h>
class contatore
{
unsigned int valore;
public:
contatore(){valore=0;};
void inc(){if(valore<65535) valore++;};
void dec(){if(valore>0) valore--;};
void set(unsigned int n){if((valore>=0)&&(valore<65535)) valore=n;};
unsigned int val(){return valore;};
}; //la definizione termina con ‘;’
main()
{
contatore c1,c2;
c2.set(10);
for(short i=0; i<10; i++){
c1.inc(); c2.inc();
}
cout << "valore finale di c1=" << c1.val() << " finale di c2=" << c2.val();
}
Output:
Valore finale di c1=10 finale di c2=20
30
//oop16.cpp
Scrittura dei metodi separatamente
#include <iostream.h>
class contatore
//tipicamente le classi sono definite in un header file .h
{
unsigned int valore;
public:
contatore();
void inc();
void dec();
void set(unsigned int n);
unsigned int val();
}; //la definizione termina con “;”
//le definizioni delle funzioni possono essere implementate nello stesso file
//in un file separato *.cpp oppure in file diversi
contatore::contatore(){valore=0;};
void contatore::inc(){if(valore<65535) valore++;};
void contatore::dec(){if(valore>0) valore--;};
void contatore::set(unsigned int n){if((valore>=0)&&(valore<65535)) valore=n;};
unsigned int contatore::val(){return valore;};
main()
{
contatore c1,c2;
c2.set(10);
for(short i=0; i<10; i++){
c1.inc(); c2.inc();
}
cout << "valore finale di c1=" << c1.val() << " finale di c2=" << c2.val();
}
31
//oop17.cpp
Le classi in C++
#include <iostream.h> // per la cout
sono un tipo di struttura
struct persona0 { //tipica dichiarazione di struttura dati in C
int eta;
//i "campi" sono pubblici per definizione
int altezza;
int peso;
};
class persona1 { //definizione di una classe di oggetti (senza metodi)
int eta;
//i campi sono PRIVATI per definizione
int altezza;
int peso;
};
class persona2 {
//aggiungo i metodi per manipolare i dati
private: // parte protetta non visibile all'esterno: SI POTREBBE OMETTERE
int eta;
int altezza;
int peso;
public: // parte visibile (interfaccia)
void set(int, int, int);
// prototipo di metodo
void get(int*, int*, int*);
// idem come sopra
void print()
//funzione inline
{ cout << eta << "\t" << altezza << "\t" << peso << "\n"; }
};
/* implementazione dei
void persona2::set(int
{
eta = e; //dentro
altezza = a;
//
peso = p;
}
metodi di persona2 */
e, int a, int p)
ai metodi della classe si possono usare
i dati privati
void persona2::get(int *e, int *a, int *p)
{
*e = eta;
*a = altezza;
*p = peso;
}
32
struct persona3 { // equivalente a class persona2
public: // parte visibile (interfaccia) SI POTREBBE OMETTERE L'ETICHETTA
void set(int, int, int); // prototipo di metodo
void get(int*, int*, int*); // idem come sopra
void print()
{ cout << eta << "\t" << altezza << "\t" << peso << "\n"; }
private: // parte protetta non visibile all'esterno;
int eta;
int altezza;
int peso;
};
/* implementazione dei metodi di persona3 (identici a persona2) */
void persona3::set(int e, int a, int p)
{
eta = e; //dentro ai metodi della classe si possono usare
altezza = a;
// i dati privati
peso = p;
}
void persona3::get(int *e, int *a, int *p)
{
*e = eta;
*a = altezza;
*p = peso;
}
/* Programma di prova delle classi */
void main()
{
persona0 antonio; persona1 luigi; persona2 giulio; persona3 matteo;
antonio.eta = 20; // OK
//luigi.eta = 23; //errore
giulio.set(20,180,70); // OK
giulio.print();
matteo.set(27,170,76); // OK
//cout << matteo.eta; // errore
int c1, c2, c3;
matteo.get(&c1,&c2,&c3);
33
cout<<"eta' di Matteo:"<<c1<<"altezza di Matteo="<<c2<<"peso di Matteo="<<c3<< endl;
}
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 “->”:
//oop18.cpp
#include <iostream.h>
#include "contatore.h"
//per la cout
//definizioni e metodi della classe contatore precedente
contatore::contatore(){valore=0;};
void contatore::incrementa(){if(valore<65535) valore++;};
void contatore::decrementa(){if(valore>0) valore--;};
void contatore::set(unsigned int n){if((valore>=0)&&(valore<65535)) valore=n;};
unsigned int contatore::val(){return valore;};
void main()
{
contatore c1;
contatore *c3=new contatore;
cout << "c1=" << c1.val() << "c3=" << c3->val() << endl;;
c1.set(10);
c3->set(5);
cout << "c3->val()=" << c3->val() << endl;
for(short i=0; i<10; i++){
c1.incrementa(); c3->incrementa();
}
cout << "valore finale di c1=" << c1.val() << " finale di c3=" << c3->val() ;
}
34
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
35
•
struttura dati astratta punto
#include <iostream.h>
//oop19.cpp
class punto
{
int x,y; //coordinate private
public:
punto(){x=0; y=0;};
//costruttore di default
punto(int a, int b) {x=a; y=b;}; //altro costruttore (overloading delle funzioni)
void MuoviPunto(int d1, int d2)
{
x+=d1; y+=d2; return;
}
void StampaPunto(){ cout << "x=" << x <<" y=" << y;}
};
main()
{
punto p1;
punto p2(1,2);
p2.MuoviPunto(1,1);
//attiva il secondo costruttore
//attiva MuoviPunto
p2.StampaPunto();
}
36
•
•
•
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
#include <iostream.h>
//oop20.cpp
class outer
{
private:
class inter
{
public:
int x;
void init(int n=0);
};
inter a,b;
public:
void init(int, int);
void stampa();
};
void outer::inter::init(int n) { x=n; return; }
void outer::init(int n1, int n2) { a.init(n1); b.init(n2); return;
void outer::stampa()
{
cout << "a=" << a.x << " b=" << b.x << endl; return; }
main()
{
outer c1, c2;
c1.init(1,1); c2.init(2,2);
c1.stampa(); c2.stampa();
}
}
37
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.
38
•
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
//
#i nc l ude <i os t r e a m. h>
c l a s s s t r i nga
{
i nt l e n;
c ha r *s t r ;
publ i c
s t r i nga ( ) { l e n=0; }
/ / c os t r ut t or e di de f a ul t
}
ma i n( )
{
s t r i nga s t r 1, s t r 2; / / a t t i va i l c os t r ut t or e di de f a u l t
}
39
•
Costruttore con argomenti: il costruttore da utilizzare dipende dalla lista degli argomenti
//oop21.cpp
#include <iostream.h>
class stringa
{
int len;
char *str;
public:
stringa(){len=0;}
//costruttore di default
stringa(const char *s) //costruttore con argomenti;
{
//s punta a stringa costante
len=strlen(s);
str=new char[len+1];
strcpy(str,s);
}
stampa(){cout << str;}
};
main()
{
stringa str1;
//attiva il costruttore di default
stringa str2("Antonio"), str3="Luigi"; //attiva il costruttore con argomenti
stringa nome=str2;
nome.stampa();
}
•
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();
...
40
• 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;
}
}
};
41
•
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
#include <iostream.h>
//oop22.cpp
class stringa
{
int len;
char *st;
public:
stringa(const char *s) { //costruttore
st=new char [strlen(s)+1]; len=strlen(s);
}
~stringa(){
//distruttore
if(len>0) delete st;
}
};
main()
{
stringa *p=new stringa("nome"); //attiva il costruttore
delete p; //elimina lo spazio dell'oggetto, non la stringa nell'heap
}
•
il distruttore, come ogni metodo, può essere chiamato esplicitamente
stringa *p=new stringa("nome");
p-> stringa:: ~stringa();
42
//esempio di ADT 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
};
43
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);
}
}
44
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
}
45
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() <<
cout << "L1hrem " << L1.hremove() <<
while( (n=L2.tremove())!=65536) cout
while( (n=L1.hremove())!=65536) cout
'\n';
'\n';
<< "L2 trem " << n << '\n';
<< "L1 hrem " << n << '\n';
}
46
//esempio di ADT 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);
};
47
// 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;
}
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();
}
p->des = NULL;
48
Autoriferimento nelle classi: l'argomento implicito 'this‘
una classe può contenere oggetti di altre classi, puntatori a oggetti di altre
classi, puntatori a oggetti della stessa classe (autoriferimento)
ogni oggetto contiene un puntatore, chiamato this, che contiene l'indirizzo
dell'oggetto stesso
nota: ogni oggetto contiene al proprio interno le variabili definite nella classe,
ma non i metodi: ci possono essere molte istanze di una classe ma una sola
istanza delle funzioni
il puntatore all'oggetto this viene passato implicitamente alle funzioni della
classe al momento della loro attivazione, per sapere qual è l'oggetto attivatore
•
•
•
•
//oop25.cpp
#include <iostream.h>
class contatore
{
unsigned int valore; //privato!
//
contatore *const this; questa definizione e' creata implicitamente
public:
contatore(); void inc(); void dec(); void set(unsigned int n); unsigned int val();
};
contatore::contatore(){this->valore=0;};
void contatore::inc(){if(this->valore<65535) this->valore++;};
void contatore::dec(){if(this->valore>0) this->valore--;};
void contatore::set(unsigned int n){ this ->valore=n;};
unsigned int contatore::val(){return this->valore;};
main()
{ //invariato
contatore c1,c2; c2.set(10); for(short i=0; i<10; i++){ c1.inc(); c2.inc(); }
cout << "valore finale di c1=" << c1.val() << " finale di c2=" << c2.val();
49
}
•
Possibilità offerta da this: costruire un metodo che ritorna il puntatore al metodo che
attivato la funzione
#include <iostream.h>
//oop26.cpp
class punto
{
int x,y;
// il compilatore inserisce in modo nascosto:punto *const this;
//const perché si vieta alla funzione di alterarne il valore
public:
punto(){x=0; y=0;}
//primo costruttore
punto(int a, int b) {x=a; y=b;}
//secondo costruttore
punto& MuoviPunto(int d1, int d2) {x+=d1; y+=d2; return *this; };
//ritorna l'oggetto stesso
punto& Stampa(){cout << " punto= " << x << ", " << y << endl; return *this;}
};
main()
{
punto p1;
// crea un'istanza della classe punto e salva il suo indirizzo
// nella variabile this=&p1
// attiva il costruttore di default: punto(&p1).
// Il costruttore viene implicitamente tradotto come
// punto(punto *const this) {this -> x=0; this->y=0;};
punto p2(1,1); //attiva il secondo costruttore: punto(&p2, 1, 1), e modifica in
//punto(punto *const this,int a,int b){this->x=a; this->y=b; return;}
p2.MuoviPunto(2, 2).Stampa();
//diventa possibile l'associazione a sinistra
p1.Stampa().MuoviPunto(1,1).Stampa();
}
Output:
punto= 3, 3
punto= 0, 0
punto= 1, 1
50
•
Altro esempio di autoriferimento: concatenazione di stringhe
#include <iostream.h>
//oop27.cpp
class stringa
{
int len;
//dati privati
char *str;
public:
//definizione dei metodi
stringa(int=0);
stringa(const char *);
stringa &concat(const stringa&);
char *visualizza();
};
//in questo modo è possibile scrivere:
main()
{
stringa a(80);
stringa b("sequenza");
stringa c(" di questa");
stringa d(" prova");
a=b.concat(c).concat(d);
cout << a.visualizza();
}
51
//implementazione dei metodi
stringa::stringa(int n)
{ len=n;
if(n>0){ str=new char (len+1);
str[0]='\0'; } }
stringa::stringa(const char *s)
{ len=strlen(s); str=new char[len+1]; strcpy(str,s); }
char* stringa::visualizza()
{ return str; }
stringa& stringa::concat(const stringa &s)
{
len += s.len;
char *temp=new char[len+1];
strcpy(temp,str);
strcat(temp, s.str);
//temp="sequenza di questa"
str=temp;
//str e' la stringa dell'oggetto che ha
attivato concat
return *this;
//ritorna l'oggetto che ha attivato
concat
}
Output:
sequenza di questa prova
52
Array di oggetti
•
definizione:
contatore c1, c2[100];
c2[10].stampa();
•
•
il costruttore e il distruttore vengono chiamati per ciascun elemento dell'array
per un vettore di oggetti appartenenti ad una classe con un costruttore, la classe
deve avere un costruttore senza argomenti
#include <stdio.h>
//oop28.cpp
class punto
{
int x1, x2;
public:
punto(int x, int y) {x1=x; x2=y;}
punto(){x1=0; x2=0;}
void visualizza(){printf("x1, y1
",x1, x2);}
};
main()
{
punto a(1,2);
punto va[20];
for(int i=1;i<20;i++) va[i].visualizza();
}
53
•
•
•
Lo specificatore friend
aggiunta all’incapsulamento e mascheramento. Ma: cautela!
normalmente: solo un insieme ben definito di messaggi può essere inviato ad
un'oggetto di una classe che deve avere i metodi sufficienti per elaborare i dati
'friend' specifica quali classi esterne possono accedere ai componenti privati
//oop29.cpp Esempio: la classe B accede direttamente ai dati privati della classe A
#include <iostream.h>
class A
{
friend class B; //indicazione unidirezionale!!
int uno, due, tre;
public:
A(){uno=0; due=0; tre=30;};
void inc(){uno++;due++;};
void lst(){cout<<"A::uno="<<uno<<" A::due="<<due<<" A::tre="<<tre<<endl; }
};
class B
{
int uno, due;
public:
B(){uno=10;due=20;};
void dec(){uno--; due--;};
void lst(A &a){cout<<"B::uno="<<uno<<" B::due="<<due<<" A::tre="<<a.tre<< endl;};
};
main()
{
A a,b; B c,d;
a.lst(); c.lst(a);
54
}
Elementi statici di una classe
•
•
un elemento dichiarato static, viene condiviso in un'unica copia da tutti i oggetti
della classe (normalmente ogni oggetto ha i propri dati!)
esempio: per conoscere quanti oggetti di una classe sono stati definiti:
#include <iostream.h>
//oop30.cpp
class punto
{
int x,y;
public:
static int n_punti;
punto(){n_punti++;};
};
int punto::n_punti=0;
main()
{
punto a, b, c;
cout << "nr oggetti:" << punto::n_punti << endl;
}
•
poiché l’elemeto statico è comune tutti gli oggetti, una modifica in un punto
modifica tutti gli altri
55
Inizializzazione di oggetti
•
•
•
Problema:
string nome(“Luigi”); nuovo=nome;
Inizializzazione memberwise
Costruttore memberwise:
nomeclasse::nomeclasse(const
nomeclasse&);
//oop31.cpp
#include <iostream.h>
class stringa
{
int len;
char *ch;
public:
stringa(){len=0;}
//costruttore di default
stringa(const char *s)
//costruttore con argomenti; s e' puntatore a stringa costante
{
len=strlen(s); ch=new char[len+1]; strcpy(ch,s);
}
stringa(const stringa&s)
//costruttore di copia memberwise
{
len=s.len; ch=s.ch;
}
void list(){cout<<ch<<endl;}
};
main()
{
stringa a, b("Luigi");
stringa c=b;
//l’oggetto c viene definito e inizializzato con l’oggetto b
c.list();
}
56
•
•
•
uso della inizializzazione memberwise:
oggetto1=oggetto2
passaggio argomenti per copia alle funzioni:
Class stringa
{
public:
//…
int val(){return len;}
};
int lung(stringa s){return s.val();}
main()
{
stringa a, b("Luigi"), c=b;
c.list();
cout << lung(c);
}
•
ritorno per valore di un oggetto:
stringa crea(int l)
{
stringa s;
//...
return s; //usa l’iniz. di copia
}
•
modalita’ per riferimento: non richiede copia locale!
57
#include <iostream.h>
//oop32.cpp
Esempio di costruttori per copia
class stringa
{
int len;
char *ch;
public:
stringa() {cout << "costruttore di default" << endl; len=0;}//costruttore di default
stringa(const char *s) //costruttore con argomenti; s e' puntatore a stringa costante
{ cout << "costr. con argomenti" << endl; len=strlen(s); ch=new char[len+1]; strcpy(ch,s);}
stringa(const stringa&s)
//costruttore di copia
{ cout << "costruttore di copia" << endl; len=s.len; ch=s.ch;}
void list(){cout<<ch<<endl;}
int val(){return len;}
void set(int n){len=n;}
void setp(char *ps){ch=ps;}
};
int lung(stringa s){return s.val();} //usa l'inizializzazione di copia
stringa crea(int l)
{ stringa s; char *ps; s.set(l); ps=new char[l+1]; s.setp(ps); return s;} //usa l’iniz. di copia}
main()
{
stringa a; //costruttore di default
stringa b("Luigi"); //costruttore con argomenti
stringa c=b; //inizializzazione di copia
c.list();
cout << lung(c); a=crea(10); cout << lung(a);
}
Output:
costruttore di default
Costr. con argomenti
costruttore di copia
Luigi
costruttore di copia
5
costruttore di default
costruttore di copia
costruttore di copia
58
10
Overloading degli operatori
•
•
•
ridefinizione di simboli del linguaggio
notazione infissa anche fra tipi diversi
limiti:
•
•
•
•
•
•
•
solo operatori gia’ definiti
l’operatore che viene ridefinito conserva le sue caratteristiche di associativita’, precedenza e numero di
argomenti
almeno uno degli argomenti deve essere di tipo classe
non si possono usare operatori che non hanno significato in C (es. **)
Specificatore: operator <operatore da sovrapporre>
operatori binari (+,-,*,/) e unari (++, --, +=, etc)
operatore binario: definito da un metodo interno con un argomento - a.add(b) – oppure
con due argomenti – a=a.add(a,b) classe C
{
priv;
...
public:
C binary_op(C);
C binary_op(C, C);
}
classe binary_op(classe arg){return classe(priv+arg.priv);}
classe binary_op(classe arg1, classe arg2){ return classe(arg1.priv+arg2.priv);}
main(){
a.binary_op(b);
a=a.binary_op(a,b);
}
59
•
operatore unario: metodo intero ad una classe senza argomenti operatore unario
U: l'espressione argomento U oppure U argomento può essere:
• argomento.U() oppure U(argomento)
•
non è possibile realizzare applicazione prefissa e postfissa
/* Uso degli operatori unari e binari */
//oop33.cpp
#include <iostream.h>
void main()
{
int a=1;
int b;
cout << "prima a= " << a << '\n';
b = a++; // l'operatore post-incremento e' unario: prima assegna e poi incrementa
cout << "dopo a++: a= " << a << " b=" << b <<'\n';
a=1;
b = ++a; /* l'operatore pre-incremento e' unario: prima incrementa e poi assegna*/
cout << "dopo ++a: a= " << a << " b=" << b <<'\n';
a=1;
b = a+1; /* l'operatore somma e' binario: prima somma e poi assegna */
cout << "dopo a+1: a= " << a << " b=" << b <<'\n';
}
60
•
overloading di operatori unari
#include <iostream.h>
//oop34.cpp
class contatore
{
unsigned int valore;
public:
contatore(){valore=0;}
void operator++(){if(valore<65535) valore++;}//overloading di ++
void operator--(){if(valore>0) valore--;}//overloading di ()
void operator()(unsigned int n){if((valore>=0)&&(valore<65535)) valore=n;}
unsigned int val(){return valore;}
};
main()
{
contatore c1,c2;
c2(10);
for(short i=0; i<10; i++){
c1++; c2++;
}
cout << "valore finale di c1=" << c1.val() << " finale di c2=" << c2.val();
}
Output:
Valore finale di c1=10 finale di c2=20
61
•
numeri complessi
//oop35.cpp
#include <stdio.h>
#include <iostream.h>
#include <stdlib.h>
#include <string.h>
class complesso
{
private:
float pr, pi;
public:
complesso(){pr=0;pi=0;}
complesso(float x,float y);
void cadd(complesso arg);
void csub(complesso arg);
void cmult(complesso arg);
void visualizza();
};
// definizione funzioni
complesso::complesso(float x, float y)
{
pr=x; pi=y; }
void complesso::cadd(complesso arg)
{
pr+=arg.pr; pi+=arg.pi; }
void complesso::csub(complesso arg)
{
pr-=arg.pr; pi-=arg.pi; }
void complesso::cmult(complesso arg)
{
float temp1, temp2;
//costruttore di default
//altro costruttore
//operazione binaria
//operazione binaria
temp1=pr*arg.pr - pi*arg.pi; temp2=pr*arg.pi + pi*arg.pr; pr=temp1; pi=temp2;
}
62
void complesso::visualizza()
{
if(pi<0)
cout << '\n' << "complesso: " << pr << "- j" << abs(pi);
else
cout << '\n' << "complesso: " << pr << "+ j" << pi;
}
main()
{
complesso a(1,3), b(2,2);
a.visualizza(); b.visualizza();
a.cadd(b); b.cadd(b);
a.visualizza(); b.visualizza();
a.csub(b);
a.visualizza(); b.visualizza();
a.cmult(b);
a.visualizza(); b.visualizza();
}
Output:
complesso:
complesso:
complesso:
complesso:
complesso:
complesso:
complesso:
complesso:
1+ j3
2+ j2
3+ j5
4+ j4
-1+ j1
4+ j4
-8+ j0
4+ j4
63
•
seconda versione
//oop36.cpp
#include <iostream.h>
#include <math.h>
class complesso
{
private:
float pr, pi;
public:
complesso(){pr=0;pi=0;}
//costruttore di default
complesso(float x,float y);
//altro costruttore
complesso cadd(complesso arg);
complesso csub(complesso arg);
complesso cmult(complesso arg);
void visualizza();
};
complesso::complesso(float x, float y){
pr=x; pi=y; }
complesso complesso::cadd(complesso arg){complesso t; t.pr=pr+arg.pr;
t.pi=pi+arg.pi; return t;}
complesso complesso::csub(complesso arg){complesso t; t.pr=pr-arg.pr; t.pi=piarg.pi; return t;}
complesso complesso::cmult(complesso arg) { complesso t; float temp1, temp2;
temp1=pr*arg.pr - pi*arg.pi; temp2=pr*arg.pi + pi*arg.pr; t.pr=temp1;
t.pi=temp2; return t;
}
64
void complesso::visualizza()
{
if(pi<0)
cout << '\n' << "complesso: " << pr << "- j" << abs(pi);
else
cout << '\n' << "complesso: " << pr << "+ j" << pi;
}
main()
{
complesso a(1,3), b(2,2);
a.visualizza(); b.visualizza();
a=a.cadd(b); b=b.cadd(b);
a.visualizza(); b.visualizza();
a=a.csub(b);
a.visualizza(); b.visualizza();
a=a.cmult(b);
a.visualizza(); b.visualizza();
}
65
•
overloading degli operatori
//oop37.cpp
#include <stdio.h>
#include <iostream.h>
#include <stdlib.h>
#include <string.h>
class complesso
{
private:
float pr, pi;
public:
complesso(){pr=0;pi=0;}
//costruttore di default
complesso(float x,float y);
//altro costruttore
complesso operator+(complesso arg);
complesso operator-(complesso arg);
complesso operator*(complesso arg);
complesso operator*(float arg);
void visualizza();
};
// definizione funzioni
complesso::complesso(float x, float y)
{ pr=x; pi=y; }
complesso complesso::operator+(complesso arg) //complesso
operator+(complesso b){ co
{ complesso t; t.pr=pr+arg.pr; t.pi=pi+arg.pi; return t;}
complesso complesso::operator-(complesso arg)
{ complesso t; t.pr=pr-arg.pr; t.pi=pi-arg.pi; return t;}
66
complesso complesso::operator*(complesso arg)
{ complesso t; float temp1, temp2;
temp1=pr*arg.pr - pi*arg.pi; temp2=pr*arg.pi + pi*arg.pr; t.pr=temp1;
t.pi=temp2; return t;
}
complesso complesso::operator*(float arg){ //prodotto con tipo diverso!
return complesso(arg*pr, arg*pi);
}
void complesso::visualizza()
{
if(pi<0)
cout << '\n' << "complesso: " << pr << "- j" << abs(pi);
else
cout << '\n' << "complesso: " << pr << "+ j" << pi;
}
main()
{
complesso a(1,3), b(2,2), c, d;
a.visualizza(); b.visualizza();
a=a+b; b=b+b;
a.visualizza(); b.visualizza();
a=a-b; a.visualizza(); b.visualizza();
a=a*b; a.visualizza(); b.visualizza();
a=a*2; //attenzione: non e’ possibile 2*a!
a.visualizza(); c=a+a; a=((a+b-c)*c)*2; a.visualizza();
}
67
Libreria <stream.h>
•
la classe ostream è definita in stream.h: l'operatore<< viene sovrapposto per
gestire l'output di tutti i tipi predefiniti o definita dell'utente. Semplificando:
class ostream
{
private:
...
public:
ostream & operator << (char *ch);
ostream & operator << (int i);
ostream & operator << (long l);
...
};
•
•
l’operazione cout << i puo’ essere vista come cout.operator<<(i)
l’operazione cout << i << j; puo’ essere vista come
cout.operator<<(i).operator<<(j)
68

Template di classi: classi generiche
il concetto di template -modello- e’ stato introdotto per creare classi generiche
//oop38.cpp
#include <iostream.h>
/* implementazione in C++ del tipo di dato astratto STACK tramite array di interi */
class stack {
private:
int *bottom;
// puntatore al fondo dello stack
int *top;
// puntatore alla cima dello stack
public:
stack()
// costruttore della classe stack
{
bottom = top = new int[100]; }
~stack()
// distruttore della classe stack
{
delete bottom;
}
void push (int c)
{ if ( (top -bottom) < 100 ) *top++ = c; }
int pop()
{
if ( --top >= bottom ) return *top; else return -1;
}
}; // end class stack
void main()
{
stack s;
int i;
for (i = 0; i < 10; i++)
for (i = 0; i < 10; i++)
s.push(i);
cout << s.pop();
}
69
•
Problema della classe stack: cambiamento di tipo:
//oop39.cpp
#include <iostream.h>
/* implementazione in C++ del tipo di dato astratto STACK tramite array di interi */
class stack {
private:
double *bottom; // puntatore al fondo dello stack
double *top;
// puntatore alla cima dello stack
public:
stack() // costruttore della classe stack
{
bottom = top = new double[100]; }
~stack()
// distruttore della classe stack
{
delete bottom; }
void push (double d)
{
if ( (top -bottom) < 100 ) *top++ = d;
}
double pop()
{
if ( --top >= bottom )
return *top;
else
return -1;
}
}; // end class stack
void main()
{
stack s;
for (short i = 0; i < 10; i++)
for (short i = 0; i < 10; i++)
}
s.push(i*3.14);
cout << s.pop() << ' ';
70
//oop40.cpp
#include <iostream.h>
/* tipo di dato astratto STACK di interi e double con TEMPLATE */
template<class tipo>
class stack {
private:
tipo *bottom;
// puntatore al fondo dello stack
tipo *top;
// puntatore alla cima dello stack
public:
stack()
// costruttore della classe stack
{ bottom = top = new tipo[100]; }
~stack()
// distruttore della classe stack
{ delete bottom;
}
void push (tipo d)
{
if ( (top -bottom) < 100 ) *top++ = d; }
tipo pop()
{
if ( --top >= bottom )
return *top;
else
return -1;
}
}; // end class stack
void main()
{
stack<int> s1;
stack<double> s2;
for (short i = 0; i < 10; i++)
s1.push(i);
for (short i = 0; i < 10; i++)
s2.push(i*3.14);
cout << "stack di interi: ";
for (short i = 0; i < 10; i++)
cout << s1.pop() << ' ';
cout << "\nstack di double: ";
for (short i = 0; i < 10; i++)
cout << s2.pop() << ' ';
}
71
Derivazione di classi
•
•
•
•
l'ereditarietà permette ai programmatori di riutilizzare una classe esistente per
costruire una gerarchia di componenti software riutilizzabile
personalizzazione della classe d'origine
Basi della ereditarietà: la classe derivata; ogni OOP gestisce in modo diverso i
dettagli
in C++:
• la classe derivata eredita/modifica tutti i metodi propri della classe d'origine
aggiungendo metodi nuovi
• gestisce una gerarchia di classi
• derivazione singola  una classe può avere un'unica classe d'origine (albero)
• derivazione multipla  una classe puo’ avere piu’ classi d’origine
• visibilità dei dati privati della classe d'origine:
• una classe derivata NON può accedere ai dati privati a meno che non sia autorizzata
farlo con lo specificatore friend o con la modalità protetta (più usata)
• ogni oggetto di classe derivata ha i propri dati privati e una propria copia di dati
privati della classe d'origine
•
sintassi:
class ClasseDerivata: ClasseOrigine
{
...
};
72
•
come viene realizzato il processo di ereditarietà?
• Controllo accesso: indicatori “public”, “private”, “protected”
• se l'indicatore viene omesso  private
• public: mantiene pubblico, privato, protected nella classe derivata ogni
componente rispettivamente pubblico, privato, protected della classe
base
•
se la classe base eredita componenti protetti privati li mantiene protetti o privati
class Base
{
int
a;
// privato nella classe base
protected:
//protetto:
int bb;
public:
//pubblico
int b;
};
class Derivata: public Base
{ int
c; //privato nella classe derivata
public:
int d;
//pubblico
int funz(){c=b+bb+d; return c;}
}
main()
{ Derivata x;
x.d=10;
x.b=20;
printf(x.funz());
// x.a, x.c non sono accessibili
73
//oop41.cpp
#include <iostream.h>
class Persona
{
protected:
int eta; char *nome;
public:
Persona(const char *n, int e){
nome=new char[strlen(n)+1];
strcpy(nome,n);
eta=e;
}
void presentati(){
cout << "sono una persona, mi chiamo " << nome << "ed ho " << eta << "anni"
<< endl;
}
};
class Studente:public Persona
{
private:
int anno; char *facolta;
public:
Studente(char *n, char *f, int e, int a):Persona(n,e){
facolta=new char[strlen(f)+1]; strcpy(facolta,f); anno=a;
}
void presentati(){
cout<<"sono uno studente di nome "<<nome<<" ho "<<eta<<"anni e sono
iscritto a "<<facolta<<" al "<<anno<<" anno di corso" << endl;
}
};
74
main()
{
Persona mario("Mario Verdi", 22);
Studente luigi("Luigi Rossi", "ingegneria", 24, 5);
mario.presentati();
luigi.presentati();
}
sono una persona, mi chiamo Mario Verdi ed ho 22anni
sono uno studente di nome Luigi Rossi ho 24 anni e sono iscritto a ingegneria al 5 anno
di corso
•
Tutti i metodi della classe d’origine possone essere inviati come messaggio agli
oggetti della classe derivata
75
Costruttori/distruttore nelle classi derivate
•
•
•
legato alla visibilità tra oggetti derivati e oggetti base
se un oggetto di classe derivata viene inizializzato, il costruttore deve
assicurarsi che venga eseguita una inizializzazione anche dell'oggetto della
classe di base-interna alla classe derivata-.
il costruttore della classe derivata attiva uno dei costruttore della classe base:
class Base
{
int a;
protected:
int bb;
public:
int b;
void Base(){a=0;}
}
class Derivata:public Base
{
int c;
public:
Derivata():Base(){c=0;}
int funz(){c=c+bb+b+d; return c;}
}
class Derivata2: public Derivata(){... public: derivata2():
Derivata()}
76
//ESEMPIO DI CLASSE BASE
//file complesso.h
class complesso {
protected:
float pr, pi;
public:
complesso(){pr=0;pi=0;}
complesso(float x,float y);
//costruttore di default
//altro costruttore
void add(complesso arg);
void sub(complesso arg);
void mpy(complesso arg);
void visualizza();
};
// definizione funzioni
complesso::complesso(float x, float y)
{
pr=x; pi=y; }
void complesso::add(complesso arg) //complesso operator+(complesso b){ co
{
pr=pr+arg.pr; pi=pi+arg.pi; }
void complesso::sub(complesso arg)
{
pr=pr-arg.pr; pi=pi-arg.pi; }
void complesso::mpy(complesso arg)
{
float temp1, temp2;
temp1=pr*arg.pr - pi*arg.pi; temp2=pr*arg.pi + pi*arg.pr; pr=temp1; pi=temp2;
}
void complesso::visualizza()
{
if(pi<0) cout << '\n' << "pr,pi " << pr << "- j" << abs(pi); else cout << '\n' << "pr,pi " << pr << "+ j" << pi; }
77
//OOP42.cpp
#include <iostream.h>
#include <stdlib.h>
#include <math.h>
#include "complesso.h"
class comp:public complesso
{
private:
float modulo;
//valori trigonometrici
float fase;
public:
comp(float x, float y):complesso(x, y) //costruttore
{
modulo=sqrt(x*x+y*y);
fase=atan(y/x);
}
void c2t()
{
float x=pr; float y=pi;
modulo=sqrt(x*x+y*y);
fase=atan(y/x);
}
void t2c()
{
pr=modulo*cos(fase); pi=modulo*sin(fase);
}
void mult(comp arg)
{
modulo=modulo*arg.modulo; fase=fase+arg.fase; }
void div(comp arg)
{
modulo=modulo/arg.modulo; fase=fase-arg.fase; }
void visualizza()
{ complesso::visualizza(); cout << " modulo=" << modulo << " fase=" << fase;}
};
main()
{
comp a(1,1); comp b(1,2);
a.visualizza();
b.visualizza();
a.mult(b); a.t2c();
a.visualizza();
a.div(b); a.t2c();
a.visualizza();
}
78
//oop43.cpp
//derivazione
#include <stdio.h>
#include <iostream.h>
#include <stdlib.h>
#include <string.h>
class complesso
{
protected:
float pr, pi;
public:
complesso(float x,float y);
//altro costruttore
complesso cadd(complesso arg);
complesso csub(complesso arg);
void visualizza();
};
// definizione funzioni
complesso::complesso(float x, float y)
{
pr=x; pi=y;
}
complesso complesso::cadd(complesso arg)
{
pr+=arg.pr;
pi+=arg.pi;
}
79
complesso complesso::csub(complesso arg)
{
pr-=arg.pr;
pi-=arg.pi;
}
void complesso::visualizza()
{
cout << '\n' << "complesso: " << pr << "+ j" << pi;
}
class comp: public complesso
//derivazione per estendere la classe
{
public:
comp(float x, float y):complesso(x, y){};//costruttore
comp cmult(comp arg);
comp cdiv(comp arg);
void visualizza(char a);
};
//
comp comp::cmult(comp arg)
{
float temp1, temp2;
temp1=pr*arg.pr - pi*arg.pi;
temp2=pr*arg.pi + pi*arg.pr;
pr=temp1; pi=temp2;
}
80
comp comp::cdiv(comp arg)
{
float temp, temp1, temp2;
temp=arg.pr*arg.pr + arg.pi*arg.pi;
temp1 = (pr*arg.pr + pi*arg.pi)/temp;
temp2 = (pr*arg.pi - pi*arg.pr)/temp;
pr=temp1; pi=temp2;
}
void comp::visualizza(char a)
{
cout << '\n' << a;
complesso::visualizza();
}
main()
{
comp a(1,2), b(2,2);
a.complesso::visualizza(); b.complesso::visualizza();
a.cadd(b); b.cadd(b);
a.visualizza('a'); b.visualizza('b');
a.csub(b);
a.visualizza('a'); b.visualizza('b');
a.cmult(b);
a.visualizza('a');
b.cdiv(b);
b.visualizza('b');
}
81
//OOP44.cpp
liste derivate
#include <stdio.h>
#include <iostream.h>
#include <stdlib.h>
#include <string.h>
#include <alloc.h>
class nodo
{
protected:
nodo *next;
int valore;
public:
nodo(){next=NULL;valore=0;}
void loadpun(nodo *a){next=a;}
nodo *getpun(){return next;}
void loadval(int a){valore=a;}
int getval(){return valore;}
};
class nodo_ext: public nodo
{
protected:
char *nome;
int flag;
public:
nodo_ext(char *n, int f):nodo(){strcpy(nome,n);flag=f;}
nodo_ext():nodo(){flag=0;nome="";}
void loadnome(char *n){nome=new char[strlen(n)+1]; strcpy(nome,n);}
char *getnome(){return nome;}
void loadflag(int a){flag=a;}
int getflag(){return flag;}
};
82
class lista
{
private:
nodo_ext *head;
public:
lista(){head=NULL;} //costruttore
void add(int val, char *n, int f);
//at the bottom
int tremove();
//toglie dalla coda e restituisce il nome
void type();
//visita dalla cima e stampa il contenuto
};
void lista::add(int val, char *n, int f)
{
nodo_ext *temp, *prec;
if(head){
temp=head;
while(temp){
prec=temp; temp=(nodo_ext *)temp->getpun();
}
prec->loadpun(new nodo_ext); prec=(nodo_ext*)prec->getpun();
prec->loadval(val); prec->loadflag(f);
prec->loadnome(n); prec->loadpun(NULL);
} else {
head=new nodo_ext; head -> loadval(val);
head -> loadflag(f); head -> loadnome(n);
head -> loadpun(NULL);
}
}
83
int lista::tremove()
{
int n; nodo_ext *temp, *prec;
temp=head;
if(head){
while(temp->getpun()) {
prec=temp; yemp=(nodo_ext*)temp->getpun();
}
n=temp->getval(); prec->loadpun(NULL); delete(temp);
return(n);
} else return(65536);
//65536 means empty
}
void lista::type()
{
nodo_ext *temp;
temp=head;
while(temp){
cout << "val=" << temp->getval() << " nome=" << temp->getnome() << " flag="
cout << temp->getflag() << '\n'; temp=(nodo_ext*)temp->getpun();
}
}
main()
{
lista L1, L2;
int n;
for(int i=0;i<5;i++) L1.add(i,"primo",0);
L1.type();
cout << "inizio a rimuovere dalla coda" << '\n';
while((n=L1.tremove())!=65536) cout << "remove L1 " << n << '\n';
}
84
•
•
Regole di visibilita’: Classe base (superclasse)  classe derivata (sottoclasse)
Ricerca di una componente interna ad una variabile di sottoclasse:
• Prima nella sottoclasse
• Poi nelle componenti ereditate
•
Funzioni ridefinite nella sottoclasse:nasconde la funzione della superclasse
Tutte le funzioni sono ancora attive! Si possono chiamare o con casting –
((superclasse)oggetto sottoclasse).funzione – o con scope –
Oggetto_sottoclasse.superclasse::funzione
•
•
Riprendiamo la classe Studente. l’abbinamento funzione-oggetto e’ fatto
STATICAMENTE dal compilatore (all’atto della compilazione) sulla base di
tipo oggetto e argomenti
Ma: con i puntatori?
Studente *giorgio; Persona *giulio;
giorgio=new Studente(“Giorgio Bianchi", "ingegneria", 23,4);
giorgio -> presentati();
•
Attenzione: i puntatori possono puntare a oggetti diversi!
giorgio -> presentati();
giulio=giorgio; giulio->presentati(); // la associazione e’ fatta sulla base del
// tipo del puntatore e non dell’oggetto!!
•
•
giulio e’ una persona ma punta ad uno studente!
delete giulio cancella solo la persona: spreco spazio
Funzioni virtuali: associazione sulla base del tipo di oggetto
85
//oop45.cpp
#include <iostream.h>
class Persona
{
protected:
int eta;
char *nome;
public:
Persona(const char *n, int e){ nome=new char[strlen(n)+1]; strcpy(nome,n); eta=e;}
virtual void presentati()
{
cout << "sono una persona, mi chiamo " << nome << " ed ho " << eta << " anni“
<< endl;
}
};
class Studente:public Persona
{
private:
int anno;
char *facolta;
public:
Studente(char *n, char *f, int e, int a):Persona(n,e)
{
facolta=new char[strlen(f)+1]; strcpy(facolta,f); anno=a;
}
virtual void presentati(){
cout<<"sono uno studente di nome "<<nome<<" ho "<<eta<<" anni e sono iscritto a
"<<facolta<<" al "<<anno<<" anno di corso" << endl;
}
};
86
main()
{
Persona mario("Mario Verdi", 22);
Studente luigi("Luigi Rossi", "ingegneria", 24, 5);
mario.presentati();
luigi.presentati();
Studente *giorgio; Persona *giulio;
giorgio=new Studente("Giorgio Bianchi", "ingegneria", 23,4);
giorgio -> presentati();
giulio=giorgio; giulio->presentati();
}
Output con funzioni virtuali:
sono una persona, mi chiamo Mario Verdi ed ho 22 anni
sono uno studente di nome Luigi Rossi ho 24 anni e sono iscritto a ingegneria al 5 anno di corso
sono uno studente di nome Giorgio Bianchi ho 23 anni e sono iscritto a ingegneria al 4 anno di corso
sono uno studente di nome Giorgio Bianchi ho 23 anni e sono iscritto a ingegneria al 4 anno di corsOutput o
Output senza funzioni virtuali
sono una persona, mi chiamo Mario Verdi ed ho 22anni
sono uno studente di nome Luigi Rossi ho 24 anni e sono iscritto a ingegneria al 5 anno di corso
sono uno studente di nome Giorgio Bianchi ho 23 anni e sono iscritto a ingegneria al 4 anno di corso
sono una persona, mi chiamo Giorgio Bianchi ed ho 23anni
87
Polimorfismo e funzioni virtuali
•
•
•
•
•
•
Polimorfismo: capacità di rispondere in modo differenziato agli stessi comandi
 realizzato con overloading delle funzioni e con le funzioni virtuali
overloading delle funzioni: la scelta della funzione da attivare è effettuata
esaminando una lista degli operandi o il tipo di oggetti tramite cui vengono
operate le richieste alle operazioni
• abbinamento statico(static binding): deciso alla compilazione
• abbinamento dinamico(dynamic binding): deciso in run-time
overloading quando gli oggetti vengono rappresentati con puntatore:
i puntatori possono puntare a oggetti di tipo diverso!
ma l'abbinamento statico si basa sul tipo di puntatore e non sul tipo di oggetto
puntato!
funzione virtuale: funzione il cui abbinamento con l'oggetto è fatto in run-time
sintassi: virtual int funz(){..........}
88
•
•
•
•
una funzione definita virtuale nella classe base in una gerarchia di derivazione,
rende virtuali tutte le funzioni con stesso prototipo e componenti la classe
derivata
l'abbinamento dinamico oggetto-funzione con le funzioni virtuali funziona solo
se gli oggetti sono gestiti con puntatore. Se l'oggetto gestito con il nome,
l'associazione e' statica.
tre casi in cui la chiamata di una funzione virtuale è risolta staticamente:
• quando la chiamata e’ effettuata con un oggetto e non con un puntatore
• quando si usa scope (::) alla classe nella chiamata con puntatore
• quando una funzione virtuale è chiamata all'interno di costruttore o
distruttore
costruttori e distruttori virtuali
• Un costruttore non puo’ essere mai dichiarato virtuale (deve essere dichiarato prima)
• Distruttori possono essere virtuali!
89
//oop46.cpp
#include <iostream.h>
class SuperficiePiana
{
protected:
float dim1;
public:
SuperficiePiana(float d) {dim1=d;}
virtual void presentati()
{ cout << “Sono una superficie piana, la mia prima dimensione e' " << dim1 ; }
virtual float Area(){ return 0;}
};
class triangolo: public SuperficiePiana
{
private:
float dim2;
public:
triangolo(float d1, float d2) : SuperficiePiana(d1)
{
dim2=d2; }
virtual void presentati()
{ cout<<“Sono un triangolo di dimensioni " << dim1 <<" e " << dim2 ; }
virtual float Area(){ return dim1*dim2/2.; }
};
class cerchio: public SuperficiePiana
{
public:
cerchio(float d1):SuperficiePiana(d1)
{}
virtual void presentati()
{ cout<< “Sono un cerchio" ; }
virtual float Area(){ return 3.14 * dim1*dim1; }
90
};
main()
{
SuperficiePiana * f1;
f1=new SuperficiePiana(2.);
f1->presentati();
cout << ". La mia area = " << f1->Area() << endl;
f1=new triangolo(2.,4.);
f1->presentati();
cout << ". La mia area = " << f1->Area() << endl;
}
Altra possibilita’:
...
stampa_info(SuperficiePiana *s)
{
s->presentati();
cout << “ La mia area=" << s->Area() << endl;
}
main()
{
SuperficiePiana f1(2);
triangolo f2(3,4);
cerchio f3(1);
stampa_info(&f1);
stampa_info(&f2);
stampa_info(&f3);
}
91
//oop47.cpp
// aggiunta della classe trapezio
class trapezio:public SuperficiePiana
{
private:
float dim2;
float dim3;
public:
trapezio(float d1, float d2, float d3):SuperficiePiana(d1)
{dim2=d2;dim3=d3;}
void presentati()
{ cout << "sono un trapezio con basi=" << dim1 << ", " << dim2 <<
" e altezza= " << dim3 ;
}
float Area(){return (dim1+dim2)*dim3/2; }
};
main()
{
SuperficiePiana f1(2);
triangolo f2(3,4);
cerchio f3(1);
trapezio f4(2,3,4);
stampa_info(&f1);
stampa_info(&f2);
stampa_info(&f3);
stampa_info(&f4);
}
92
Polimorfismo al lavoro
•
•
•
•
•
•
•
I vantaggi del polimorfismo
Esempio: creazione di una ADT lista per realizzare un censimento
(categorie di persone, dati sulle persone etc..)
Esempio di lista senza polimorfismo senza istruzione friend
Esempio di lista senza polimorfismo con istruzione friend
Esempio di modifica della lista lista senza polimorfismo e con istruzione
friend
Esempio di lista polimorfica con istruzione friend
Esempio di modifica della lista polimorfica
93
•
Lista non polimorfica senza costrutto friend: header file
//lista2.h
#include <iostream.h>
enum tipo_nodo{studente,lavoratore, pensionato} ;
struct tipo_studente{ int matricola; int anno_di_corso;};
class nodo
{
private:
char cognome[10];
char nome[10];
int anni;
int codice_fiscale;
tipo_nodo attivita;
union {tipo_studente s; float stipendio; int anni_quiescenza;};
nodo *next;
public:
nodo(char *co, char *no, int a, int cf, tipo_nodo t);
nodo();
void set_attivita(tipo_nodo t);
void set_cognome(char *c);
void set_nome(char *n);
void set_anni(int a);
void set_cf(int cf);
void set_matricola(int m);
void set_anno_corso(int ac);
void set_stipendio(float s);
void set_quiescenza(int q);
void set_next(nodo * t);
94
tipo_nodo get_attivita();
char * get_cognome();
char * get_nome();
int get_anni();
int get_cf();
int get_matricola();
int get_anno_corso();
float get_stipendio();
int get_quiescenza();
nodo * get_next();
};
class lista
{
private:
nodo *radice;
public:
lista();
void insert(nodo *n);
void remove(char *c);
void stampa();
};
//fine header file lista2.h
95
//oop48.cpp. Implementazione dei metodi lista non polimorfica, senza friend
#include <iostream.h>
#include "lista2.h"
//implementazione dei costruttori
nodo::nodo(char *co, char *no, int a, int cf, tipo_nodo t)
{ strcpy(cognome,co); strcpy(nome,no); anni=a; codice_fiscale=cf; attivita=t; next=0; }
nodo::nodo()
{ strcpy(cognome,"\0"); strcpy(nome,"\0"); anni=0; codice_fiscale=0; next=0; }
//implementazione dei metodi di nodo
void nodo::set_attivita(tipo_nodo t) { attivita=t; }
void nodo::set_cognome(char *c){ strcpy(cognome,c); }
void nodo::set_nome(char *n) { strcpy(nome,n); }
void nodo::set_anni(int a) { anni=a; }
void nodo::set_cf(int cf) { codice_fiscale=cf; }
void nodo::set_matricola(int m) { s.matricola=m; }
void nodo::set_anno_corso(int ac) { s.anno_di_corso=ac; }
void nodo::set_stipendio(float s) { stipendio=s; }
void nodo::set_quiescenza(int q) { anni_quiescenza=q;}
void nodo::set_next(nodo * t) { next=t;}
tipo_nodo nodo::get_attivita() { return attivita; }
char * nodo::get_cognome() { return cognome; }
char * nodo::get_nome() { return nome; }
int nodo::get_anni() { return anni; }
int nodo::get_cf() { return codice_fiscale; }
int nodo::get_matricola() { return s.matricola; }
int nodo::get_anno_corso() { return s.anno_di_corso; }
float nodo::get_stipendio() { return stipendio; }
int nodo::get_quiescenza() { return anni_quiescenza;}
nodo * nodo::get_next() { return next; }
96
//oop48.cpp (continuazione).
lista::lista() //implementazione del costruttore di lista
{ radice=0; }
void lista::insert(nodo *n) //implementazione dei metodi di lista
{
nodo *nuovo, *p, *q;
nuovo=new nodo(n->get_cognome(),n->get_nome(),n->get_anni(),n->get_cf(),n->get_attivita());
switch(nuovo->get_attivita())
{
case studente:
nuovo->set_matricola(n->get_matricola()); nuovo->set_anno_corso(n->get_anno_corso());
break;
case lavoratore:
nuovo->set_stipendio(n->get_stipendio());
break;
case pensionato:
nuovo->set_quiescenza(n->get_quiescenza());
break;
}
nuovo->set_next(0);
p=q=radice;
if(p==0) {p=radice=nuovo;}
else { while(p!=0)
{q=p; p=p->get_next();}
q->set_next(nuovo);
}
}
void lista::remove(char *c) {}
//remove non implementata in questa versione per semplicita’
97
//oop48.cpp (continuazione).
void lista::stampa()
{
nodo *p;
p=radice;
while(p!=0)
{
cout<<p->get_cognome()<<" "<<p->get_nome()<<" anni "<<p->get_anni()<<" CF "<<p->get_cf()<< " ";
switch (p->get_attivita())
{
case studente:
cout << "studente ";
cout<<"matricola n."<<p->get_matricola()<<"anno di corso "<<p->get_anno_corso() << endl;
break;
case lavoratore:
cout << "lavoratore "; cout << "stipendio:"<<p->get_stipendio() << endl;
break;
case pensionato:
cout << "pensionato "; cout << "anni di quiescenza" << p->get_quiescenza() << endl;
}
p=p->get_next();
}
}
98
//oop48.cpp (continuazione e main).
main()
{ //vari modi di creare l'oggetto nodo. 1. mediante il costruttore con argomenti
lista l;
nodo t("Teseo","Giovanni",70,1234,pensionato);
t.set_quiescenza(5);
l.insert(&t);
//2. mediante i metodi per modificare i campi privati
t.set_cognome("Terreni");
t.set_nome("Piero");
t.set_attivita(studente);
t.set_anni(30);
t.set_cf(2222);
t.set_matricola(230);
t.set_anno_corso(5);
l.insert(&t);
//3.mediante puntatori e lo statement new
nodo *t1;
t1=new nodo("Pallino", "Pinco", 23, 3333, lavoratore); t1->set_stipendio(1000);
l.insert(t1);
t1=new nodo("Tizio", "Gianni", 33, 3222, lavoratore); t1->set_stipendio(1500);
l.insert(t1);
l.stampa();
}
Output
Teseo Giovanni anni 70 CF 1234 pensionato anni di quiescenza5
Terreni Piero anni 30 CF 2222 studente matricola n.230anno di corso 5
Pallino Pinco anni 23 CF 3333 lavoratore stipendio:1000
Tizio Gianni anni 33 CF 3222 lavoratore stipendio:1500
99
//lista1.h
//lista non polimorfica con uso dello statement FRIEND. Header file:
#include <iostream.h>
enum tipo_nodo{studente,lavoratore, pensionato} ;
struct tipo_studente{ int matricola; int anno_di_corso;};
class nodo
{
friend class lista;
private:
char cognome[10]; char nome[10]; int anni; int codice_fiscale; tipo_nodo attivita;
union {tipo_studente s; float stipendio; int anni_quiescenza;}; nodo *next;
public:
nodo(char *co, char *no, int a, int cf, tipo_nodo t);
nodo();
void set_attivita(tipo_nodo t);
void set_cognome(char *c);
void set_nome(char *n);
void set_anni(int a);
void set_cf(int cf);
void set_matricola(int m);
void set_anno_corso(int ac);
void set_stipendio(float s);
void set_quiescenza(int q);
};
class lista
{
private:
nodo *radice;
public:
lista();
void insert(nodo *n);
void remove(char *c);
void stampa();
100
};
//oop49.cpp
implementazioni dei metodi della lista non polimorfica.
#include <iostream.h>
#include "lista1.h"
nodo::nodo(char *co, char *no, int a, int cf, tipo_nodo t)
{ strcpy(cognome,co); strcpy(nome,no); anni=a; codice_fiscale=cf; attivita=t; next=0;
nodo::nodo()
{ strcpy(cognome,"\0"); strcpy(nome,"\0"); anni=0; codice_fiscale=0; next=0; }
void nodo::set_attivita(tipo_nodo t) { attivita=t; }
void nodo::set_cognome(char *c) { strcpy(cognome,c); }
void nodo::set_nome(char *n) { strcpy(nome,n); }
void nodo::set_anni(int a) { anni=a; }
void nodo::set_cf(int cf) { codice_fiscale=cf; }
void nodo::set_matricola(int m) { s.matricola=m; }
void nodo::set_anno_corso(int ac) { s.anno_di_corso=ac; }
void nodo::set_stipendio(float s) { stipendio=s; }
void nodo::set_quiescenza(int q) { anni_quiescenza=q;}
lista::lista() { radice=0; }
void lista::insert(nodo *n)
{
nodo *nuovo, *p, *q;
nuovo=new nodo( n->cognome, n->nome, n->anni, n->codice_fiscale, n->attivita );
switch(n->attivita)
{ case studente:
nuovo->s.matricola=n->s.matricola; nuovo->s.anno_di_corso=n->s.anno_di_corso; break;
case lavoratore:
nuovo->stipendio=n->stipendio; break;
case pensionato:
nuovo->anni_quiescenza=n->anni_quiescenza; break;
}
nuovo->next=0; p=q=radice;
if(p==0) {p=radice=nuovo;} else { while(p!=0){q=p; p=p->next;} q->next=nuovo; }
101
}
//oop49.cpp (continua)
implementazioni dei metodi della lista non polimorfica.
void lista::remove(char *c)
{}
void lista::stampa()
{
nodo *p;
p=radice;
while(p!=0)
{
cout<<p->cognome<<" "<<p->nome<<" anni "<<p->anni<<" CF "<<p->codice_fiscale<<" ";
switch (p->attivita)
{
case studente:
cout << "studente ";
cout<<"matricola n."<<p->s.matricola<<"anno di corso "<<p->s.anno_di_corso << endl;
break;
case lavoratore:
cout << "lavoratore con stipendio="<<p->stipendio << endl;
break;
case pensionato:
cout << "pensionato ";
cout << "anni di quiescenza" << p->anni_quiescenza << endl;
}
p=p->next;
}
}
102
//oop49.cpp (continua)
implementazioni del main della lista non polimorfica.
main()
{
lista l; nodo t;
t.set_cognome("Teseo"); t.set_nome("Giovanni"); t.set_attivita(pensionato);
t.set_anni(70);
t.set_cf(1234);
t.set_quiescenza(5);
l.insert(&t);
t.set_cognome("Pierucci");
t.set_nome("Piero");
t.set_attivita(lavoratore);
t.set_anni(40); t.set_cf(1111); t.set_stipendio(2000);
l.insert(&t);
t.set_cognome("Terreni");
t.set_nome("Piero"); t.set_attivita(studente);
t.set_anni(30); t.set_cf(2222); t.set_matricola(23); t.set_anno_corso(5);
l.insert(&t);
l.stampa();
}
Output
Teseo Giovanni anni 70 CF 1234 pensionato anni di quiescenza5
Pierucci Piero anni 40 CF 1111 lavoratore con stipendio=2000
Terreni Piero anni 30 CF 2222 studente matricola n.23anno di corso 5
103
//inserimento nella lista di una categoria aggiuntiva. Lista non polimorfica con friend
// lista3.h
#include <iostream.h>
enum tipo_nodo{studente,lavoratore, pensionato, disoccupato} ;
//modifica
struct tipo_studente{ int matricola; int anno_di_corso;};
struct disocc{ int mesi_disoccupazione; int nr_impieghi;};
//modifica
class nodo
{
friend class lista;
private:
char cognome[10]; char nome[10]; int anni; int codice_fiscale; tipo_nodo attivita;
union {tipo_studente s; float stipendio; int anni_quiescenza; disocc dis;}; // modifica
nodo *next;
public:
nodo(char *co, char *no, int a, int cf, tipo_nodo t);
nodo(); ~nodo();
void set_attivita(tipo_nodo t); void set_cognome(char *c); void set_nome(char *n);
void set_anni(int a); void set_cf(int cf); void set_matricola(int m);
void set_anno_corso(int ac); void set_stipendio(float s); void set_quiescenza(int q);
void set_disoc(int d); void set_impie(int i);
};
class lista
{
private:
nodo *radice;
public:
lista(); ~lista(); void insert(nodo *n); void remove(char *c); void stampa();
};
104
//oop50.cpp
//Modifica: inserimento di una categoria aggiuntiva. Implementazione metodi
//Lista non polimorfica con friend!
#include <iostream.h>
#include "lista3.h"
nodo::nodo(char *co, char *no, int a, int cf, tipo_nodo t)
{ strcpy(cognome,co); strcpy(nome,no); anni=a; codice_fiscale=cf; attivita=t; next=0; }
nodo::nodo()
{ strcpy(cognome,"\0"); strcpy(nome,"\0"); anni=0; codice_fiscale=0; next=0; }
nodo::~nodo(){};
void nodo::set_attivita(tipo_nodo t) { attivita=t; }
void nodo::set_cognome(char *c) { strcpy(cognome,c); }
void nodo::set_nome(char *n) { strcpy(nome,n); }
void nodo::set_anni(int a) { anni=a; }
void nodo::set_cf(int cf) { codice_fiscale=cf; }
void nodo::set_matricola(int m) { s.matricola=m; }
void nodo::set_anno_corso(int ac) { s.anno_di_corso=ac; }
void nodo::set_stipendio(float s) { stipendio=s; }
void nodo::set_quiescenza(int q) { anni_quiescenza=q;}
void nodo::set_disoc(int d) { dis.mesi_disoccupazione=d;}
void nodo::set_impie(int i) { dis.nr_impieghi=i;}
lista::lista()
{ radice=0; }
105
// oop50.cpp (continua)
lista::~lista()
{ // questo e’ il metodo piu’ complesso. Quali sono i due metodi semplici?
nodo *p,*q;
do{
p=q=radice; while(p->next!=0){q=p;p=p->next;} q->next=0; delete p;
} while(q!=radice);
delete radice;
}
void lista::insert(nodo *n)
{
nodo *nuovo, *p, *q;
nuovo=new nodo(
n->cognome, n->nome, n->anni, n->codice_fiscale, n->attivita );
switch(n->attivita)
{
case studente:
nuovo->s.matricola=n->s.matricola; nuovo->s.anno_di_corso=n->s.anno_di_corso; break;
case lavoratore: nuovo->stipendio=n->stipendio; break;
case pensionato: nuovo->anni_quiescenza=n->anni_quiescenza; break;
case disoccupato:
//modifica
nuovo->dis.mesi_disoccupazione=n->dis.mesi_disoccupazione;
nuovo->dis.nr_impieghi=n->dis.nr_impieghi;
break;
}
nuovo->next=0; p=q=radice;
if(p==0) {p=radice=nuovo;} else
{ while(p!=0){q=p; p=p->next;} q->next=nuovo; }
}
106
// oop50.cpp (continua)
void lista::remove(char *c)
{// in questo esempio non e' stata scritta. Si lascia per esercizio }
void lista::stampa()
{
nodo *p;
p=radice;
while(p!=0)
{ cout<<p->cognome<<" "<<p->nome<<"\t anni "<<p->anni<<" CF "<<p->codice_fiscale<< " ";
switch (p->attivita)
{
case studente:
cout << "studente ";
cout<<"matricola nr."<<p->s.matricola<<" anno di corso "<<p->s.anno_di_corso << endl;
break;
case lavoratore:
cout << "lavoratore "; cout << "stipendio="<<p->stipendio << endl; break;
case pensionato:
cout << "pensionato ";
cout << "anni di quiescenza=" << p->anni_quiescenza << endl; break;
case disoccupato:
//modifica
cout<<"disoccupato da ";
cout<<p->dis.mesi_disoccupazione<<" mesi. nr. impieghi="<<p->dis.nr_impieghi<<endl;
}
p=p->next;
}
}
107
// oop50.cpp (continua)
main()
{
lista l;
nodo t1("Teseo", "Giovanni", 70, 1234, pensionato);
t1.set_quiescenza(5);
l.insert(&t1);
nodo t2("Pierucci", "Piero", 40, 1111, lavoratore); t2.set_stipendio(2000);
l.insert(&t2);
nodo t3("Terreni", "Piero", 30, 2222, studente); t3.set_matricola(23);
t3.set_anno_corso(5);
l.insert(&t3);
nodo t4("Rossi", "Luigi", 25, 3276, disoccupato);
t4.set_disoc(5); t4.set_impie(0);
l.insert(&t4);
l.stampa();
}
Output:
Teseo Giovanni
anni 70 CF 1234 pensionato anni di quiescenza=5
Pierucci Piero anni 40 CF 1111 lavoratore stipendio=2000
Terreni Piero anni 30 CF 2222 studente matricola nr.23 anno di corso 5
Rossi Luigi
anni 25 CF 3276 disoccupato da 5 mesi. nr. impieghi=0
108
//lista4.h
//lista polimorfica. Header file
#include <iostream.h>
class persona
{
friend class lista;
protected:
char cognome[10];
char nome[10];
int anni;
int codice_fiscale;
persona *ptr;
persona *next;
public:
persona(char *co, char *no, int a, int cf);
persona();
~persona();
void set_cognome(char *c);
void set_nome(char *n);
void set_anni(int a);
void set_cf(int cf);
virtual void stampa();
virtual void insert();
};
109
//lista4.h (continua)
class studente:public persona
{
friend class lista;
private:
int matricola;
int anno_di_corso;
public:
studente(char *co, char *no, int a, int cf, int mat, int an):persona(co, no, a, cf)
{ matricola=mat; anno_di_corso=an; }
studente():persona()
{ matricola=0; anno_di_corso=0;}
void set_matricola(int m);
void set_anno_corso(int ac);
void stampa();
void insert();
};
class lavoratore:public persona
{
friend class lista;
private:
float stipendio;
public:
lavoratore(char *co, char *no, int a, int cf, float s):persona(co, no, a, cf){stipendio=s;}
lavoratore():persona() { stipendio=0;}
void set_stipendio(float s);
void stampa(); void insert();
};
110
//lista4.h (continua)
class pensionato:public persona
{
friend class lista;
private:
int anni_quiescenza;
public:
pensionato(char *co, char *no, int a, int cf, int q):persona(co, no, a, cf)
{ anni_quiescenza=q; }
pensionato():persona()
{ anni_quiescenza=0;}
void set_quiescenza(int q);
void stampa();
void insert();
};
class lista
{
private:
persona *radice;
public:
lista();
~lista();
void inserisci(persona *n);
void rimuovi(char *c);
void stampa();
};
111
//oop51.cpp. Lista polimorfica
//Implementazione dei metodi della lista polimorfica
#include <iostream.h>
#include "lista4.h"
persona::persona(char *co, char *no, int a, int cf)
{ strcpy(cognome,co); strcpy(nome,no);anni=a; codice_fiscale=cf; next=0; }
persona::persona()
{ strcpy(cognome,"\0"); strcpy(nome,"\0"); anni=0; codice_fiscale=0; next=0; }
persona::~persona(){};
void persona::set_cognome(char *c) { strcpy(cognome,c); }
void persona::set_nome(char *n) { strcpy(nome,n); }
void persona::set_anni(int a) { anni=a; }
void persona::set_cf(int cf) { codice_fiscale=cf; }
void persona::insert()
{ ptr=new persona(cognome, nome, anni, codice_fiscale); }
void persona::stampa()
{ cout << cognome << " " << nome << "\t anni " << anni << " CF= " << codice_fiscale ;}
void studente::set_matricola(int m) { matricola=m; }
void studente::set_anno_corso(int ac) { anno_di_corso=ac;}
void studente::stampa()
{ persona::stampa();
cout << " Studente, matricola=" << matricola << " anno di corso="<<anno_di_corso<<endl;
}
void studente::insert()
{ ptr=new studente(cognome, nome, anni, codice_fiscale, matricola, anno_di_corso); }
112
//oop51.cpp. Lista polimorfica
void lavoratore::set_stipendio(float s)
{ stipendio=s; }
void lavoratore::stampa()
{
persona::stampa(); cout << " Lavoratore, stipendio=" << stipendio <<endl;
}
void lavoratore::insert()
{ ptr=new lavoratore(cognome, nome, anni, codice_fiscale, stipendio); }
void pensionato::set_quiescenza(int q)
{ anni_quiescenza=q;}
void pensionato::stampa()
{ persona::stampa(); cout << " Pensionato, anni di quiescenza=" << anni_quiescenza <<endl;}
void pensionato::insert()
{ ptr=new pensionato(cognome, nome, anni, codice_fiscale, anni_quiescenza); }
lista::lista()
{ radice=0; }
lista::~lista()
{ // naturalmente per cancellare lo spazio degli oggetti bisogna avere una lista
// bidirezionale. Si lascia per esercizio
persona *p,*q;
do{ p=q=radice; while(p->next!=0){q=p;p=p->next;} q->next=0; delete p; } while(q!=radice);
delete radice;
}
113
//oop51.cpp (continua). Lista polimorfica
void lista::inserisci(persona *n)
{
persona *p, *q;
p=q=radice;
n->insert();
if(p==0) {p=radice=n->ptr;} else { while(p!=0){q=p; p=p->next;} q->next=n->ptr;
}
}
void lista::rimuovi(char *c)
{// in questo esempio non e' stata scritta. Si lascia per esercizio }
void lista::stampa()
{
persona *p;
p=radice; while(p!=0) { p->stampa(); p=p->next; }
}
main()
{
lista l; persona t;
studente st("Terreni", "Piero", 30, 2222, 23, 5);
lavoratore la("Pierucci", "Piero", 40, 1111, 2000);
pensionato pen("Rossi", "Luigi", 70, 3276, 5);
l.inserisci(&st);
l.inserisci(&la); l.inserisci(&pen);
l.stampa();
}
114
//Introduzione di una classe aggiuntiva. Lista polimorfica. Header file
//lista5.h
#include <iostream.h>
class persona
{
friend class lista;
protected:
char cognome[10];char nome[10];int anni;int codice_fiscale;persona *ptr;persona *next;
public:
persona(char *co, char *no, int a, int cf);
persona(); ~persona();
void set_cognome(char *c); void set_nome(char *n); void set_anni(int a);
void set_cf(int cf);
virtual void stampa(); virtual void insert();
};
class studente:public persona
{
friend class lista;
private:
int matricola; int anno_di_corso;
public:
studente(char *co, char *no, int a, int cf, int mat, int an):persona(co, no, a, cf)
{ matricola=mat; anno_di_corso=an; }
studente():persona() { matricola=0; anno_di_corso=0;}
void set_matricola(int m); void set_anno_corso(int ac); void stampa(); void insert();
};
115
//lista5.h (continua)
class lavoratore:public persona
{
friend class lista;
private:
float stipendio;
public:
lavoratore(char *co, char *no, int a, int cf, float s):persona(co, no, a, cf)
{ stipendio=s; }
lavoratore():persona() { stipendio=0;}
void set_stipendio(float s); void stampa(); void insert();
};
class pensionato:public persona
{
friend class lista;
private:
int anni_quiescenza;
public:
pensionato(char *co, char *no, int a, int cf, int q):persona(co, no, a, cf)
{ anni_quiescenza=q; }
pensionato():persona() { anni_quiescenza=0;}
void set_quiescenza(int q); void stampa(); void insert();
};
116
//lista5.h (continua)
Classe aggiunta nell’header file
class disoccupato:public persona
{
friend class lista;
private:
int mesi_disoccupazione;
int nr_impieghi;
public:
disoccupato(char *co, char *no, int a, int cf, int md, int ni):persona(co, no, a, cf)
{ mesi_disoccupazione=md; nr_impieghi=ni; }
disoccupato():persona() { mesi_disoccupazione=0; nr_impieghi=0; }
void set_disoccupazione(int md) { mesi_disoccupazione=md; }
void set_impieghi(int ni) { nr_impieghi=ni; }
void stampa() { persona::stampa(); cout << " Disoccupato da " << mesi_disoccupazione;
cout << " mesi. Nr. impieghi=" << nr_impieghi << endl; }
void insert()
{
ptr=new disoccupato(cognome,nome,anni,codice_fiscale,mesi_disoccupazione,nr_impieghi);
};
};
class lista
{ private:
persona *radice;
public:
lista(); ~lista(); void inserisci(persona *n); void rimuovi(char *c); void stampa();
};
117
//oop52.cpp
//implementazione dei metodi della lista polimorfica
#include <iostream.h>
#include "lista5.h"
persona::persona(char *co, char *no, int a, int cf)
{ strcpy(cognome,co); strcpy(nome,no); anni=a; codice_fiscale=cf; next=0; }
persona::persona()
{ strcpy(cognome,"\0"); strcpy(nome,"\0"); anni=0; codice_fiscale=0; next=0; }
persona::~persona(){};
void persona::set_cognome(char *c) { strcpy(cognome,c); }
void persona::set_nome(char *n) { strcpy(nome,n); }
void persona::set_anni(int a) { anni=a; }
void persona::set_cf(int cf) { codice_fiscale=cf; }
void persona::insert() { ptr=new persona(cognome, nome, anni, codice_fiscale); }
void persona::stampa()
{ cout << cognome << " " << nome << "\t anni " << anni << " CF= " << codice_fiscale ;}
void studente::set_matricola(int m) { matricola=m; }
void studente::set_anno_corso(int ac) { anno_di_corso=ac;}
void studente::stampa()
{
persona::stampa();
cout << " Studente, matricola=" << matricola << " anno di corso="<<anno_di_corso<<endl;
}
void studente::insert()
{ ptr=new studente(cognome, nome, anni, codice_fiscale, matricola, anno_di_corso); }
118
//oop52.cpp (continua)
void lavoratore::set_stipendio(float s) { stipendio=s; }
void lavoratore::stampa()
{ persona::stampa(); cout << " Lavoratore, stipendio=" << stipendio <<endl; }
void lavoratore::insert()
{ ptr=new lavoratore(cognome, nome, anni, codice_fiscale, stipendio); }
void pensionato::set_quiescenza(int q) { anni_quiescenza=q;}
void pensionato::stampa()
{ persona::stampa(); cout << " Pensionato, anni di quiescenza=" << anni_quiescenza <<endl;}
void pensionato::insert()
{ ptr=new pensionato(cognome, nome, anni, codice_fiscale, anni_quiescenza); }
lista::lista()
{ radice=0; }
lista::~lista()
{ // questa versione e’ la piu’ complessa possibile. Quali altre versioni semplici si possono utilizzare?
persona *p,*q;
do{ p=q=radice; while(p->next!=0){q=p;p=p->next;} q->next=0; delete p; } while(q!=radice);
delete radice;
}
void lista::inserisci(persona *n)
{ persona *p, *q;
p=q=radice; n->insert();
if(p==0) {p=radice=n->ptr;} else { while(p!=0){q=p; p=p->next;} q->next=n->ptr; }
}
void lista::rimuovi(char *c)
{// in questo esempio non e' stata scritta. Si lascia per esercizio }
119
//oop52.cpp (continua)
void lista::stampa()
{
persona *p;
p=radice;
while(p!=0)
{
p->stampa(); p=p->next;
}
}
main()
{
lista l; persona t;
studente st("Terreni", "Piero", 25, 2222, 23, 5);
lavoratore la("Pierucci", "Piero", 40, 1111, 2000);
pensionato pen("Rossi", "Luigi", 70, 3276, 5);
disoccupato dis("Ferri", "Luigi", 40, 2323, 8, 1);
l.inserisci(&st);
l.inserisci(&la);
l.inserisci(&pen);
l.inserisci(&dis);
l.stampa();
}
120
Vantaggi del polimorfismo
•
•
•
•
•
•
•
Permette di scrivere codice generico che si adatta automaticamente alle
specializzazioni future
Permette di creare nuovi metodi senza cambiare il resto del codice
Semplice estensione del codice pre-esistente  riutilizzabilita’ del codice
Attenzione: se l’impostazione e’ corretta, si puo’ cambiare molto
lavorando poco…
…se si modifica troppo il codice originale, l’impostazione e’ sbagliata!
Modifiche concentrate  migliore manutenzione
Attenzione: i vantaggi si pagano con una certa perdita di efficienza!
121
Derivazione multipla di classi
•
•
•
Finora, le gerarchie di classi erano formate da una unica superclasse
Derivazione multipla: pluralita’ di superclassi
Es. superclasse Studente, superclasse Lavoratore  sottoclasse StudenteLavoratore che deriva da entrambe!
Inoltre: sia Studente che Lavoratore derivano da Persona
Quindi:
Persona
/
\
Studente
Lavoratore
\
/
Studente-Lavoratore
•
Sintassi:
class Persona
{…}
class Studente:public Persona
{…}
class Lavoratore:public Persona
{…}
class Studente_Lavoratore:public Studente, public Lavoratore
{…}
122
//oop53.cpp
derivazione multipla
#include <iostream.h>
class Persona
{
protected:
int eta;
char *nome;
public:
Persona(const char *n, int e){ nome=new char[strlen(n)+1]; strcpy(nome,n); eta=e;}
void presentati()
{
cout << "sono una persona, mi chiamo " << nome << " ed ho " << eta << " anni"
<< endl;
}
};
class Studente:public Persona
{
private:
int anno;
char *facolta;
public:
Studente(char *n, char *f, int e, int a):Persona(n,e)
{
facolta=new char[strlen(f)+1]; strcpy(facolta,f); anno=a;
}
void presentati(){
cout<<"sono uno studente di nome "<<nome<<" ho "<<eta<<" anni e sono iscritto a
"<<facolta<<" al "<<anno<<" anno di corso" << endl;
}
};
123
class Lavoratore:public Persona
{
private:
int impresa;
float stipendio;
public:
Lavoratore(char *n, int e, int i, float s):Persona(n,e)
{
impresa=i;
stipendio=s;
}
void presentati(){
cout<<"sono un lavoratore di nome "<<nome<<" ed ho "<<eta<<" anni. Lavoro in
"<<impresa<<" e guadagno " << stipendio <<endl;
}
};
class Stud_Lav:public Studente, public Lavoratore
{
private:
int corso_serale;
public:
Stud_Lav(char *n, int e, int i, float s, int a, char *f, int c): Studente(n, f, e, a),
Lavoratore(n, e, i, s)
{ corso_serale=c; }
void presentati()
{
cout <<"sono uno studente lavoratore, e sono al corso serale "<< corso_serale << endl;
}
};
124
main()
{
Persona mario("Mario Verdi", 22);
Studente luigi("Luigi Rossi", "ingegneria", 24, 5);
mario.presentati();
luigi.presentati();
Studente *giorgio; Persona *giulio;
giorgio=new Studente("Giorgio Bianchi", "ingegneria", 23,4);
giorgio -> presentati();
giulio=giorgio; giulio->presentati();
Lavoratore antonio("Antonio Pierucci", 28, 5, 1.7);
antonio.presentati();
Stud_Lav piero("Piero Pieri", 28, 6, 1.7, 1, "ingegneria", 1);
piero.presentati();
}
Output
sono una persona, mi chiamo Mario Verdi ed ho 22 anni
sono uno studente di nome Luigi Rossi ho 24 anni e sono iscritto a ingegneria al 5 anno di corso
sono uno studente di nome Giorgio Bianchi ho 23 anni e sono iscritto a ingegneria al 4 anno di corso
sono una persona, mi chiamo Giorgio Bianchi ed ho 23 anni
sono un lavoratore di nome Antonio Pierucci ed ho 28 anni. Lavoro in 5 e guadagno 1.7
125
sono uno studente lavoratore, e sono al corso serale 1
Scarica

OOP_C++ - UniNa STiDuE