Sei sulla pagina 1di 41

C++

Storia del C++

1980: Bjarne Stroustrup produce le prime versioni del


linguaggio, note come C con classi in AT&T Bell
Laboratories
inizialmente si tratta solo una precompilazione che
trasforma il codice in C
1987: inizia lo sforzo di standardizzazione del C++

-2-

C e C++

C++ derivato dal C e pu considerarsi unestensione di


esso
la compatibilit, anche se problematica, stata
mantenuta perch:

milioni di linee di codice di funzioni di libreria in C possono essere


utilizzate in C++

si minimizzano i problemi legati ad imparare un nuovo linguaggio

-3-

Differenze tra C++ e Java (1)

Filosofia

C++:

compatibilit con il C,

prestazioni,
potenza

Java:

semplicit,
robustezza,

portabilit

-4-

Differenze tra C++ e Java (2)

template
ereditariet

prestazioni

Sostituzione di funzioni inline

semplice e multipla

possibilita di mascherare
linterfaccia dellantenato

controllo diretto della


memoria

allocazione statica della


memoria

espressivit

Sovraccaricamento operatori

Uso del const nella


definizione dei metodi

-5-

Allocazione statica vs.


allocazione dinamica

Nascita e morte di una variabile

In C++ i tipi semplici e


le classi si
comportano allo
stesso modo (anche
sintatticamente)

#include<iostream.h>
#include <string>
main()
{
int i=3;
string s1("hello");

in Java gli oggetti


seguono regole
diverse

if (i < 0)
{
float x(6.8);
string s2("negative");
x = -i;
cout << x;
cout << s2<<\n;
}
cout << i<<\n;
cout << s1<<\n;

nota

cout<< per scrivere


sullo standard output
}

// costruttore per i
// costruttore per s1

// costruttore per x
// costruttore per s2

// distruttore per x e s2

// distruttore per i e s1
-7-

Assegnamento tra oggetti

assegnare un oggetto ad un
altro corrisponde al copiarne
lo stato
dopo lassegnamento

loggetto s1 esiste ancora ed


immutato

loggetto s2 ha cambiato stato

s2 ed s1 rimangono due oggetti


distinti con il medesimo stato

#include<iostream.h>
#include <string>
main()
{
string s1("hello");
string s2("bye");
cout<<s2<<endl;
s2=s1;
s2[0]=H;

in Java lassegnamento tra


riferimenti

cout<<s1<<endl;
cout<<s2<<endl;
}
-8-

Passaggio di oggetti a funzioni

il passaggio dei parametri


avviene sia per valore che per
indirizzo

per valore (default):


void modifica(string s)

per indirizzo:
void modifica(string &s)

un oggetto passato per valore


viene ricopiato, come accade
per i tipi primitivi
hello
hello

#include<iostream.h>
#include <string>
void modifica(string s){
s[0]= H;
}
main()
{
string s1("hello");
cout<<s1<<endl;
modifica(s1);
cout<<s1<<endl;
}

output
-9-

Array di oggetti

gli array possono effettivamente


contenere i dati degli oggetti e non
soltanto riferimenti come in Java

gli array a loro volta possono


essere allocati sia staticamente che
dinamicamente

Data
+ int giorno
+ int mese
+ int anno

conferenze:

se si vuole poter dichiarare un array


di oggetti di una classe per cui
esistano costruttori, la classe deve
avere un costruttore richiamabile
senza lista di argomenti.
esempio:
Data conferenze[10];

21
5
1998
7
3
1996

- 10 -

Allocazione dinamica della memoria

il C++ fornisce due elementi


essenziali alla gestione dinamica
della memoria

il tipo puntatore
gli operatori new e delete

un puntatore una variabile


contenente un indirizzo di
memoria (o reference)
loperatore & fornisce lindirizzo
di una variabile

p=&i;
1233
10
1234

i
p

1234
1235
1236
1237

- 11 -

Dichiarazione e uso dei puntatori

puntatore allintero
memorizzato in k

#include<iostream.h>
#include <string>
main()
{
int *i;
int k=5;
int *j=&k;
string *s;

allocazione
dinamica di un
intero
liberazione della
memoria allocata
dinamicamente

i=new int;
*i=5;
s=new string("pippo");
cout<<*s<< <<*i;
delete s; delete i;

puntatore ad intero

allocazione dinamica
di una stringa

}
- 12 -

Aritmetica dei puntatori

i puntatori possono essere incrementati


e decrementati con gli usuali operatori.
Incrementare un puntatore significa
spostarlo al prossimo elemento del suo
tipo in memoria
il valore di p viene incrementato
dalloperatore ++ esattamente della
dimensione di un intero

le variabili array sono implicitamente dei


puntatori (simile a Java!)

attenzione !

int *p;
int vettore[10];
p = vettore;
*p = 1;
p++;
*p = 2;
cout << vettore[0]
<< vettore[1];
}

basta sbagliare un indice per corrompere


la memoria con effetti imprevedibili

- 13 -

Problemi di deallocazione (1)

In C++ non ce` il garbage collector come in Java.


Tutte gli oggetti creati dinamicamente vanno cancellati
esplicitamente.
D1 non pi raggiungibile!!
Data
Esempio:
+ int giorno
+ int mese
+ int anno

Data
d1 =
d2 =
d1 =

d1

d2

+ 26
+1
+ 1999

+ 27
+1
+ 1999

*d1, *d2;
new Data(26, 1, 1999);
new Data(27, 1, 1999);
d2;
d1

d2

- 14 -

Problemi di deallocazione (2)

esempio: shallow copy di un oggetto con

Stringa

puntatori

- char *caratteri
- int numeroCaratteri
~Stringa() {
delete(caratteri); }

int main()
{
Stringa s1(10);

Stringa s2(20);
s1 = s2;
// errore: si
// dealloca 2
// volte s2.p !!!

s1
78

78

10

10

s2

215

s1 = s2

78

20

10

s2

215

215

215
20

s1
215

20

20

20

- 15 -

Classi

Esempio
#include <stdlib.h>
#include <stdio.h>

string::string(const char* S = "") {


s = (char*)malloc(strlen(S) + 1);
strcpy(s, S);
}

class string {
private:
char* s;

main() {
string s1("hello"), s2("there");
s1.print();
s2.print();
}

public:
string(const char* s = "");
~string() { free(s); }
void print() const {
printf("%s\n",s);
}

simile alla classe Java

nota:

};

distruttore

uso del C

metodi inline
separazione tra dichiarazione
e implementazione

parametri const
metodi const

- 17 -

Aggregazione
#include <iostream.h>

class Data {
int giorno; int mese; int anno;
public:
Data(int g=0, int m=0,int a=0):
giorno(g), mese(m), anno(a) {;};
};

class Appuntamento{
Data giorno;
string motivo;
public:
Appuntamento(int g, int m, int a, char * mot):
giorno(g,m,a), motivo(mot) {;};
};
main(){
Appuntamento imp(12, 5, 97, "riunione");
}

due oggetti, luno attributo


dellaltro, vengono creati e
distrutti insieme
la struttura dati
delloggetto contenuto
inclusa nella struttura dati
delloggetto contenitore
in Java non possibile
nota:

lista di inizializzazione

parametri di default

- 18 -

Aggregazione
main(){
Appuntamento imp(12, 5, 97, "riunione");
}
imp.giorno:
imp:

12
5
97

riunione

- 19 -

Amicizia
class Pluto
{
//...
friend class Pippo;
friend int funzioneAmica();
friend char Tom::metodoAmico();
//...
}

la classe Pluto concede alla classe Pippo (ai suoi


metodi), alla funzione funzioneAmica ed al solo metodo
metodoAmico della classe Tom di accedere ai propri
attributi privati

- 20 -

Overloading operatori

Sovraccaricamento degli operatori

grazie alloverloading
gli oggetti possono
essere trattati come i
tipi primitivi
attenzione alla
semantica che si
associa alloperatore

si rischia di confondere
lutilizzatore della classe

esempio di operatori:

- + += = == ++ [ ]
< << ! cast * ->

class Data {
//...
public:
int operator < (const Data& d);
};
void main()
{
Data a, b;
//...
if (a < b)
cout << a pi vecchio di b <<endl;
};

- 22 -

Sovraccaricamento degli operatori


(esempio)
#include <iostream.h>

Complex operator + (const Complex& c2)


const

class Complex {

{
Complex temp (re + c2.re, im + c2.im);

double re;
double im;

return temp;
}

public:
Complex(double reale = 0, double imm = 0) {

void Stampa(void) const {

re = reale;
im = imm;

cout << re << + << im << i << endl;


}

};
};

nota:

metodi const
- 23 -

Sovraccaricamento degli operatori


(esempio)

problema: ci sono dei casi


in cui non funziona
perch?

main()
{
Complex c1(3.0);
Complex c2(1.0, 2.0);
c1.Stampa();
c2.Stampa();
Complex c3 = c1 + c2;
c3.Stampa();

conversione implicita tramite il


costruttore:
il 7 diventa un Complex

c3=c2+7; // FUNZIONA??
c3=7+c2; // NO!
}

- 24 -

Somma tra complex globale

ridefinizione della somma tra complex come funzione


indipendente dalla classe complex
complex operator+ (const complex& lhs, const complex& rhs) {
return complex (lhs.real() + rhs.real(), lhs.imag() + rhs.imag());
}

controindicazione:
potrebbe essere necessario rendere loperatore
friend della classe

una classe pu mostrare i propri segreti ad un insieme di


funzioni e/o classi dopo averle dichiarate amiche.
- 25 -

Input output in C++

la libreria IOSTREAM fornisce le


operazioni di input/output e
definisce gli stream di default:
cin, cout e cerr

linput si realizza con loperatore


>> abbinato a uno stream di
ingresso

loutput si realizza con loperatore


<< abbinato a uno stream di
uscita

gli operatori di input/output si


comportano adeguatamente
secondo il tipo del valore che si
vuole leggere o scrivere

#include <iostream.h>
void main()
{
double reale;
int intero;
cout << \nFornire un valore reale: ;
cin >> reale;
cout << \nFornire un valore intero: ;
cin >> intero;
cout << \n\nReale: << reale << endl;
cout << Intero: << intero << endl;
}

- 26 -

Operatori << e >> per tipi specifici


#include <iostream.h>

class Complex {
double re, im;
public:
Complex(double reale = 0, double imm = 0) {
re = reale; im = imm;
};
Complex operator + (const Complex& c2) const{

Complex temp (re + c2.re, im + c2.im); return temp;


}
friend ostream& operator << (ostream& os, const Complex& co);
};
ostream& operator << (ostream& os, const Complex& co){
os << ( << co.re << , << co.im << ); return os;
}

int main()
{ Complex c1(3.0); cout<<c1;}

gli operatori << e


>> possono
essere ridefiniti
per ogni nuovo
tipo
nota

loperatore <<
non un
metodo

spesso deve
essere reso
amico della
classe

- 27 -

Ereditariet semplice,
polimorfismo e binding

Ereditariet
#include<iostream.h>
class Veicolo {
public:
void mess() {cout << "Veicolo Generico" << endl;}
};

Veicolo
+ mess()

class VeicoloPrivato : public Veicolo {


public:
void mess() {cout << "Veicolo Privato" << endl;}
};

VeicoloPrivato

class VeicoloCommerciale : public Veicolo {


public:
void mess() {cout << "Veicolo Commerciale" << endl;}
};

+ mess()
VeicoloCommerciale
+ mess()

- 29 -

Polimorfismo e tipo di binding


main() {
Veicolo *vehicle;
VeicoloPrivato mycar;
VeicoloCommerciale myvan;

giustificazione output:

il binding statico: la versione


da eseguire del metodo
mess() viene scelta in base al
tipo statico del puntatore e non
in base al tipo delloggetto
puntato (tipo dinamico)

il C++ utilizza il binding statico


per le funzioni membro
normali, il binding dinamico
implementato attraverso l'uso
delle funzioni membro virtual

vehicle=&mycar;
vehicle->mess();
vehicle=&myvan;
vehicle->mess();
}

output
Veicolo Generico
Veicolo Generico

- 30 -

Polimorfismo e tipo di binding

esempio di binding dinamico:


#include<iostream.h>
class Veicolo {
public:
virtual void mess() {cout << "Veicolo Generico" << endl;}
};
class VeicoloPrivato : public Veicolo {
public:
void mess() {cout << "Veicolo Privato" << endl;}
};
class VeicoloCommerciale : public Veicolo {
public:
void mess() {cout << "Veicolo Commerciale" << endl;}
};
void f(Veicolo v) {
v.mess();
}
- 31 -

Polimorfismo e tipo di binding


main() {
Veicolo *vehicle;
VeicoloPrivato mycar;
VeicoloCommerciale myvan;
vehicle=&mycar;
vehicle->mess();
vehicle=&myvan;
vehicle->mess();
f(mycar);
}

output

perch nellinvocazione
di f(mycar) non stato
utilizzato il binding
dinamico ?

il metodo era
cos definito:
void f(Veicolo v) {
v.mess();
}

Veicolo Privato
Veicolo Commerciale
Veicolo Generico
- 32 -

polimorfismo tra variabili non


puntatori
Veicolo

main(){
Veicolo generico;
VeicoloCommerciale commerciale;

+ int targa
+ mess()

generico.targa=1556;
commerciale.targa=9888;
commerciale.peso=1000;

VeicoloCommerciale
+ int targa
+ int peso

generico.mess();
commerciale.mess();

output
generico=commerciale;
generico.mess();
}

+ mess()

Veicolo Generico, 1556


Veicolo Commerciale, 9888, 1000
Veicolo Generico, 9888
- 33 -

polimorfismo tra variabili non


puntatori

lassegnamento tra due oggetti di classi differente possibile quando


loggetto a destra sottoclasse delloggetto di sinistra (= regola del
polimorfismo)

lassegnamento tra due oggetti (e non due reference!) equivale alla


copia dello stato

attenzione: si perdono dei dati nellassegnamento (il padre ha meno


attributi del figlio)

i due oggetti non cambiano tipo e continuano ad essere distinti.


Quindi il binding non pu che essere statico

per la stessa ragione il polimorfismo non funzionava con la funzione f:

void f(Veicolo v) {
v.mess();
}
- 34 -

Rimozione di operazioni nelle classi


derivate

ogni metodo della super


classe dovrebbe essere
parte dellinterfaccia delle
sotto classi

in C++ possibile
nascondere uno o pi
elementi pubblici del padre
lesigenza di eliminare un
metodo in una sotto classe
normalmente indica un
cattivo progetto della
gerarchia di classi

Errore in compilazione!

#include<iostream.h>
class Veicolo {
public:
virtual double velocitaDiVolo() const {return 12;};
};
class VeicoloTerrestre : public Veicolo {
virtual double velocitaDiVolo() const {return 0;};
public:
};
main(){
VeicoloTerrestre *vt = new VeicoloTerrestre;
Veicolo *v=new Veicolo;
cout<< vt->velocitaDiVolo()<<endl;
cout<<v->velocitaDiVolo()<<endl;
}
- 35 -

Rimozione di operazioni nelle classi


derivate

si pu forzare lesecuzione del metodo privato in vt ?

il polimorfismo continua a valere e quindi:


main(){
VeicoloTerrestre *vt = new VeicoloTerrestre;
Veicolo *v;
v=vt;
cout<<v->velocitaDiVolo()<<endl;
}

in questo caso un progetto corretto sarebbe stato di definire una classe


base senza velocitaDiVolo e aggiungere una sotto classe veicoloAereo
con tale membro
- 36 -

Ereditariet di codice ed interfacce

ereditando possibile modificare la visibilit dei membri


ereditati utilizzando le tre parole chiave public, protected e
private nella dichiarazione di ereditariet
cio possibile ereditare in tre maniere differenti, per quel
che riguarda la visibilit dei membri ereditati, da una
super classe

- 37 -

Le tre varianti
di eredit semplice

nella derivazione di una classe (B) la classe base (A) pu


essere ereditata public, protected o private.
Esempio:
a) class B : public A { ... };
b) class B : protected A { ... };
c) class B : private A { ... };

regole di visibilit dei membri ereditati:


membri in A

membri in B, se B eredita da A in maniera ...


public

protected

private
private

public

public

protected

protected

protected

protected

private

non accessibile

non accessibile

private

non accessibile

- 38 -

Si perde il polimorfismo

polimorfismo e
binding dinamico
sono persi
ereditariet di
codice ma NON di
interfaccia: le due
classi coinvolte
definiscono due tipi
indipendenti

ERRORE IN
COMPILAZIONE

#include<iostream.h>
class Veicolo {
public:
virtual double velocitaDiVolo() const {return 12;};
};
class VeicoloTerrestre : protected Veicolo {
public:
virtual double velocitaDiVolo() const {return 0;};
};
main(){
VeicoloTerrestre *vt = new VeicoloTerrestre;
Veicolo *v;
cout<<vt->velocitaDiVolo()<<endl;
v=vt;
cout<<v->velocitaDiVolo()<<endl;
}

- 39 -

Ereditariet multipla

Ereditariet multipla
permette di creare classi ibride che ereditano membri da due o pi
classi. Esempio:

class Venditore {
protected:
char zona[50];
};
class Manager {
protected:
int livello;
};

Venditore

Manager

Mgr_Vendite

class Mgr_Vendite : public Venditore, public Dirigente {


};

Eredita da
entrambi i suoi
ascendenti, quindi
contiene sia
zona[50] che
livello

In Java luso dellereditarieta` multipla viene parzialmente sostituito


dalluso delle interfaccie

- 41 -

Ereditariet multipla: problemi


class Base1 {

class Base2 {

//...
public:
int doppione;
//...
};

//...
public:
int doppione;
//...
};

class Derivata : public Base1, public Base2 {


//};

in questo caso ho un conflitto di nomi dovuto al fatto che


doppione compare sia in Base1 che in Base2
posso risolvere il problema utilizzando ::
- 42 -

Ereditariet multipla: problemi


#include<iostream.h>
class Base1 {
public:
int doppione;
};

main(){
Derivata d;
d.Base1::doppione=7;
d.Base2::doppione=3;
cout<<d.totale()<<endl;
}

class Base2 {
public:
int doppione;
};
class Derivata : public Base1, public Base2 {
public:
int totale(){
return Base1::doppione + Base2::doppione;
}
};
- 43 -

Ereditariet multipla da una classe con


un antenato comune

pu succedere che una classe erediti da due classi con


un antenato comune

class Impiegato {
protected:
char nome[30];
};
class Venditore : public Impiegato {};
class Manager : public Impiegato {};
class MgrVendite : public Venditore, public Manager {};

Vede due volte


Impiegato tra i suoi
antenati, quindi
eredita due volte il
campo nome[30]

Impiegato

Impiegato

Venditore

Manager

Mgr_Vendite

- 44 -

Ereditariet multipla:
classi base virtuali

in caso di antenati comuni permettono di evitare di


ereditare due volte gli stessi membri
Impiegato

class Impiegato {
protected:
char nome[30];
};
class Venditore : virtual public Impiegato {};
class Manager : virtual public Impiegato {};
class MgrVendite : public Venditore, public Manager {};
Vede una sola volta Impiegato
tra i suoi ascendenti, quindi
eredita una sola volta il campo
nome[30]

Venditore

Manager

Mgr_Vendite

- 45 -

Template

Genericit

nella programmazione O-O lobiettivo primario la


riusabilit. Servono quindi strumenti che permettano una
generalizzazione.
problema: costruito il tipo di dati astratto Lista di Interi, lo si
vorrebbe riusare (senza modifiche) anche per trattare liste di
altri tipi, anche complessi
obiettivo: si vuole poter definire delle collezioni generiche
che non siano legate ad un particolare tipo di dati
strumenti possibili:

Macro

Classi astratte

Template
- 47 -

Il concetto di
genericit

Scopo della genericit definire una classe (o una


funzione) senza specificare il tipo di uno o pi dei suoi
membri (parametri)
Una definizione:
La genericit labilit di definire moduli parametrici. Un tale
modulo, chiamato modulo generico, non direttamente
utilizzabile; si tratta bens di uno schema di modulo. Nella maggior
parte dei casi, i parametri rappresentano tipi. I moduli effettivi,
chiamati istanze del modulo generico, sono ottenuti fornendo tipi
effettivi per ogni parametro generico
[B.Meyer]

- 48 -

Genericit attraverso luso di macro:


classi generiche

// Lista.h

Vantaggi / svantaggi

modificando la direttiva #define si


pu avere una classe Lista per
differenti tipi

Lista.h

nella stessa applicazione non


possibile avere istanze di Lista che
contengono oggetti di tipo diverso

class Lista {
TYPE dati[50];
// ...
};

// Main.cpp

#define TYPE int


#include Lista.h
Main.cpp

// ...
Lista ListaInteri;
// ...

- 49 -

Genericit attraverso ereditariet e


polimorfismo

combinando luso di ereditariet e


polimorfismo possibile ottenere
contenitori generici
class Stack {
GenericObject *data[50];
int num;
public:
Stack() : num(0) {}
void Push(GenericObject *g) {
data[num++]=g;
}
GenericObject *Pop() {
return data[--num];
}
};

GenericObject

class IntObject : public GenericObject {


public:
int data;
IntObject(int n) : data(n) { }
};
class FloatObject : public GenericObject {
public:
float data;
FloatObject(float f) : data(f) { }
};

- 50 -

Genericit attraverso ereditariet e


polimorfismo
main() {
Stack intStack;
intStack.Push(new IntObject(22));

funziona, ma:

devo sapere il tipo


delloggetto che estraggo.
Pu essere necessario
definire un campo tag che
indichi il tipo effettivo
delloggetto

devo ricorrere al down


casting, che pu dare
problemi impredicibili in
caso di errore

intStack.Push(new IntObject(78));
cout << ((IntObject *) intStack.Pop())->data;
cout << ((IntObject *) intStack.Pop())->data;
Stack floatStack;
floatStack.Push(new FloatObject(1.3));
cout << ((FloatObject *) floatStack.Pop())->data;
}

- 51 -

Genericit attraverso luso di template

i template sono funzioni o classi la cui definizione e implementazione


sono parametriche rispetto a uno o pi tipi

il compilatore sostituisce i tipi usati come parametri formali con i tipi


passati come parametri attuali al momento dell'utilizzo della funzione
(o della classe) generica, generando automaticamente il codice
necessario

l'utilizzo di funzioni (o classi) template un meccanismo per


implementare un numero infinito di sovraccaricamenti potenziali di
una funzione (o di una classe)

- 52 -

Funzioni template
#include <iostream.h>
template <class Tipo>
Tipo max(const Tipo &a, const Tipo &b) {
return (a > b) ? a : b;
}
main() {
double d1 = 1.2, d2=5.6;
int r1(15), r2(5);
cout<<"Il valore maggiore tra: " << d1 << " e "<< d2 <<" " << max(d1,d2) << endl;
cout<<"Il valore maggiore tra: " << r1 << " e " << r2 <<" " << max(r1,r2) << endl;
int i=max(r1,d2);
cout << i << endl;

funziona??

- 53 -

Funzioni template: vantaggi

con il costrutto <class T> qualsiasi tipo pu essere utilizzato: tipi


semplici o classi
equivale alla definizione di uno schema di funzione: al variare del
parametro ho infinite potenziali funzioni definite
solo per le funzioni effettivamente create a partire dal template
viene generato codice
se serve, la funzione pu essere ridefinita:
char * max(char * a, char * b) {
return (strcmp(a,b) > 0 ? a : b); }

viene fatto un controllo sui tipi:


int i; float c;
max(i, c);

// Errore segnalato in compilazione !!!

- 54 -

Template per
classi generiche

oltre a funzioni template


possibile definire classi template

Dichiarazione di una
classe generica

Definizione di un metodo
generico

template <class T>


class Vettore {
T * dato;
int dim;
public:
Vettore(int);
T& operator[](int);
};

template <class T> Vettore<T>::Vettore(int n) {...};

Definizione di unistanza
della classe generica

Vettore<int> vet(5);

- 55 -

Esempio: Stack<class T>


#ifndef __STACK_H
#define __STACK_H
template <class T> class Stack {
T data[50];
int num;
public:
Stack(void): num(0) { }
Stack.h

void Push(const T&);


T Pop(void);
int Number();
int Empty();
};
#endif
- 56 -

Esempio: Stack<class T>


#include "Stack.h"

template <class T>


T Stack<T>::Pop(void) {

template <class T>


void Stack<T>::Push(const T &elem) {

return data[--num];
}

data[num++] = elem;
}

template <class T>


int Stack<T>::Number() {
return num;
}
template <class T>
int Stack<T>::Empty() {
return (num == 0);

Stack.cpp

}
- 57 -

Esempio: Stack<class T>


#include <iostream.h>

while ( ! intStack.Empty() ) {
cout << intStack.Pop() << endl;

#include Stack.cpp

cout << complexStack.Pop() << endl;


}

main(void) {
Stack<int> intStack;

intStack.Push(25);
intStack.Push(12);
Complex c1(3.0, 2.0), c2(1.0, -3.0);
main.cpp

Stack<Complex> complexStack;
complexStack.Push(c1);
complexStack.Push(c2);

Nota

viene incluso il file stack.cpp


e non stack.h

- 58 -

Problemi con i template

se il codice della classe template contiene operazioni sul tipo


generico, queste devono essere tutte permesse sul tipo
effettivo passato come parametro.

esempio:
template <class T> T max(T a, T b) { return ( (a > b) ? a : b ); }
Complex c1, c2;
Complex c3 = max(c1, c2); // Errore !!!! Complex non
// definisce loperatore >

soluzione:

definire tutte le operazioni necessarie sul tipo


definire una versione specifica per quel tipo delle operazioni
necessarie

- 59 -

Template: aspetti avanzati

possibile dichiarare pi parametri del template


template <class T, class Q>
class TabellaRelazioni { ... };

i parametri del template non devono essere necessariamente dei tipi,


possono anche essere delle stringhe di caratteri, dei nomi di funzione
delle espressioni costanti (con possibili valori di default)
template <class T, int Dimensione = 512>
class Stack { ... };

in questo modo posso allocare lo spazio necessario al momento della


compilazione (non nello heap)

N.B.: i parametri del template che non sono tipi devono essere delle
costanti

- 60 -

Template e polimorfismo
a confronto

una funzione polimorfica se uno dei suoi parametri pu


assumere diversi tipi di dati che devono essere derivati da
quello del parametro formale
una funzione template solo uno schema di funzione. La
clausola template un generatore automatico di funzioni
overloaded
una funzione una funzione template solo se preceduta
da una specifica clausola template. Queste funzioni sono
quindi progettate, definite e implementate con lidea della
genericit in mente

- 61 -

Template e polimorfismo
a confronto

una funzione polimorfica si adatta dinamicamente a


parametri di tipo diverso. Una funzione template deve
essere compilata staticamente in versioni diverse per
soddisfare parametri di tipo diverso
le funzioni polimorfiche devono usare puntatori (o
reference)
le funzioni template operano anche con i tipi aritmetici
la genericit data dal polimorfismo limitata alle gerarchie
(perci viene detta constrained)
NOTA: in C++, a differenza di Java, non esiste ununica
classe da cui tutte le altre sono derivate
- 62 -

Template e polimorfismo
a confronto

la genericit dei template non ha restrizioni (perci viene


detta unconstrained).
le funzioni template richiedono come tipo una classe che
abbia definito tutte le funzioni membro usate nella
funzione template, mentre con il polimorfismo si assicura
questo comportamento.
per entrambi i casi la correttezza delle chiamate
controllata al momento della compilazione.
i template tendono a generare un codice eseguibile pi
grande essendo le funzioni replicate.

- 63 -

Eccezioni

Eccezioni

poche differenze concettuali con le eccezioni di Java


possibile non dichiararle nelle interfacce dei metodi

il compilatore non costringe il programmatore a gestire le


situazioni eccezionali

non esistono sorgenti implicite

le eccezioni sono state aggiunte in un secondo momento

C++ decisamente meno robusto di Java, perch non troviamo:

controllo riferimenti nulli

controllo indici array

non esiste una gerarchia precostituita di eccezioni

le eccezioni non hanno un comportamento uniforme

- 65 -

Organizzazione
dei file di un
programma C++

Stile di programmazione

un programma C++ un insieme di classi, procedure e


dati
come organizzare i file di unapplicazione

usare file separati per ogni modulo

dichiarare le classi nellheader file .h

implementare le classi nei file .cpp o .cc o .C

evitare linclusione multipla di header file

direttiva #ifndef al precompilatore

- 67 -

Relazione di inclusione

Immagine.cpp

Immagine.h

prima di linkare il
programma necessario
assicurarsi che tutti i moduli
modificati siano stati
ricompilati

main.cpp

Triangolo.cpp

Triangolo.h

il meccanismo delle
inclusioni crea dipendenze
non banali

soluzione: make file


#ifndef __TRIANGOLO_H
#define __ TRIANGOLO _H

Punto.cpp

Punto.h

#endif
- 68 -

Standard Template
Library

STL - storia

libreria di componenti sviluppata a partire dal 1992 nei


laboratori della HP
autori:
Alexander Stepanov
Meng Lee
STL stata adottata nel luglio 1994 come parte della
libreria standard dallANSI/ISO C++ Standards Committee
intento: dimostrare la possibilit di disaccoppiare gli
algoritmi dalle strutture dati senza perdere efficienza (una
libreria inefficiente non viene utilizzata)

- 70 -

Scomposizione ortogonale dello


spazio dei componenti
Algoritmi
(ordinamento, ricerca, unione)

Contenitori
(lista, array, coda)
Tipi
(char, int, double)

algoritmi, tipi e
contenitori sono tre
dimensioni
indipendenti

un contenitore pu
contenere qualsiasi
tipo

un algoritmo si applica
a qualsiasi contenitore

- 71 -

STL

libreria ampia ed efficiente


componenti principali:

contenitori
contenitori che racchiudono oggetti, come vettori, insiemi e code(sono
classi template)

iteratori

algoritmi

oggetti per accedere agli elementi racchiusi in un contenitore


operazioni applicabili a qualsiasi contenitore, come ordinamento,
ricerca ed unione (sono funzioni template)

non necessario conoscerne i dettagli per utilizzarla

- 72 -

Contenitori

tutti i contenitori si espandono automaticamente


(allocazione dinamica della memoria - come
java.util.Vector)
gli oggetti contenuti hanno un ordine ( possibile
accedere sequenzialmente agli elementi)
differenze tra i diversi contenitori:

come sono memorizzati internamente gli oggetti e quindi il costo


delle operazioni di inserimento, cancellazione ed accesso

primitive per creare e manipolare la sequenza

- 73 -

Contenitori

tre categorie di contenitori:

sequenziali
organizzano un insieme finito di oggetti dello stesso tipo in modo
strettamente sequenziale

vector, deque, list

associativi
forniscono la possibilit di accedere velocemente ai dati in base al
valore di chiavi

set, multiset, map, multimap

adattatori
incapsulano un contenitore per fornirne una interfaccia diversa

stack, queue, priority_queue


- 74 -

Contenitori

STL usa pattern ricorrenti: estremamente facile


cambiare in una applicazione il tipo di contenitore (per
migliorare le prestazioni):
le operazioni per inserire gli elementi nei contenitori
sono il pi possibile omogenee (es., push_back() e
push_front())
laccesso agli elementi inseriti pu avvenire tramite
iteratori ( consigliabile)
nellimplementazione delle STL non stata utilizzata
lereditariet (omogeneit ottenuta con convenzioni sui
nomi)
- 75 -

Iteratori

gli iteratori permettono laccesso ad una sequenza senza


che sia necessario conoscerne la struttura

Interfaccia unica per qualsiasi contenitore

utilizzo di pi iteratori contemporaneamente

corrispondono a java.util.Enumeration

- 76 -

Iteratori

in base alle loro funzionalit (metodi esportati) possono


essere suddivisi in cinque categorie:

Pi semplici

Input e Output
Forward
Bidirectional
Random access

(minori funzionalit)

Pi potenti
(inglobano le
funzionalit dei
precedenti)

- 77 -

Esempio
tipico utilizzo di un contenitore
#include <vector>
using namespace std;

shape

int main() {
draw( )
vector<shape*> shapes;
shapes.push_back(new circle);
shapes.push_back(new square);
shapes.push_back(new triangle);
for(vector<shape*> ::iterator i = shapes.begin();
circle
triangle
square
i != shapes.end(); i++)
(*i)->draw();
for(vector<shape*> ::iterator j = shapes.begin();
j != shapes.end(); j++)
delete *j;
return 0;
in Java avremmo scritto:
}
for(Enumeration i = shapes.elements();i.hasMoreElements();)
i.nextElement().draw();
- 78 -

Algoritmi
funzioni template studiate per essere applicate a qualsiasi
contenitore

quattro categorie:

-1-

operazioni che non alterano lordine degli elementi nel contenitore


for_each, find, find_if, count, ecc...

-2-

operazioni che alterano lordine degli elementi del contenitore


copy, swap, rotate, reverse, ecc...

-3-

operazioni di ordinamento (o correlate)


sort, binary_sort, ecc...

-4-

operazioni numeriche generiche


Accumulate, transform, replace, remove, ecc...

- 79 -

Esempio: ordinamento
#include <vector>
#include <algo.h>
#include <iostream.h>

//-- STAMPA IL CONTENUTO


while (first != last)
cout << *first++ << "\n";

int main() {
vector<int> v(3);
vector<int>::iterator first = v.begin();
vector<int>::iterator last = v.end();

first = v.begin();
sort(first,last); //--ORDINAMENTO

//-- STAMPA IL CONTENUTO


cout <<endl;
while (first != last)
cout << *first++ << "\n";

v[0] = 5;
v[1] = 2;
v[2] = 7;
}

gli algoritmi interagiscono con il contenitore tramite gli


iteratori
- 80 -

Riferimenti STL

David Mussers STL-page:


http://www.cs.rpi.edu/~musser/stl.html

Mummits STL Newbie guide:


http://www.xraylith.wisc.edu/~khan/software/stl/STL.newbie.html

- 81 -

STL e JDK
STL

non ancora standard e


diffusa
dedicata ai contenitori
estremamente
coerente
efficiente

JDK

standard (tutti gli ambienti


con il logo Java
dovrebbero supportarla)
copre diversi domini
applicativi
coerente
nata con il linguaggio
carente sul versante
contenitori (Vector, BitSet,
Hashtable, Stack)
- 82 -

Potrebbero piacerti anche