Estratto del documento

Teoria - Programmazione B

Stream come parametri di funzioni

es: funzione copia file (parametri stream sorgente e destinazione)

void copia_file(ifstream &S, ofstream &D)

{ char c;

c = s.get();

while (!s.eof())

{ d.put(c);

c = s.get();

}

return;

}

int main()

{ ...

ifstream f1;

f1.open("prova.txt");

ofstream f2;

f2.open("copia.prova.txt"); //copia_file (f1,cout) (stampa a video)

copia_file (f1,f2);

}

ifstream / ofstream sono sottoclassi (derivate) delle classi generali istream / ofstream (sottotipi).

In c++, se un parametro di una funzione f è di tipo/classe t è possibile passare a f (parametro di tipo

t un tipo t' sottotipo di t (conviene però dichiarare prima il parametro di tipo t).

In particolare:

void copia_file (istream &s, ostream &d)

{…}

cin:istream, cout:ostream parametri della copia_file

Leggi e stampa classe razionale

class razionale {

public:

void stampa (ostream &dest) { //void operator << (...)

dest << n << "/" << d;

return;

}

void leggi (istream &sorg) { //void operator >> (...)

sorg >> num >> sep >> den;

...

}

};

int main() {

razionale a(2,3);

...

a.stampa(cout); //stampa a video

...

ofstream f_out;

f_out.open("risultati.txt");

a.stampa(f_out) //stampa su file

}

Overloading operatori >> e <<

1) operatori >> e << come funzioni proprie della classe razionale (stesso di prima cambiando il

nome del void)

int main() {

razionale a(2,3);

cout << a; //errore

a << cout; //OK

cout.operator<<(a); //?

}

2) gli operatori >> e << li definiamo come funzioni esterne alla classe razionale → n e d non

accessibili → funzioni proprie della classe razionale leggi e stampa mentre operator >> e operator

<< si limitano a richiamare leggi e stampa.

Teniamo la classe razionale di partenza:

- ostream &

void operator << (ostream &dest, razionale x) {

x.stampa(dest);

return;

} //return dest

- istream &

void operator >> (istream &sorg, razionale &x) {

x.leggi(sorg);

return; } //return sorg

int main() {

razionale a(2,3);

...

cout << a; //operator << (cout,a) OK

razionale b;

cin >> b; //operator >> (cin,b) OK

}

3) miglioramento della 2: gli operatori >> e << li definiamo come funzioni esterne con tipo delle

funzioni ostream / istream

→ cout << a << endl;

“ostream &” → reference return;

In generale int f(int x) {… return z; } il valore di z è copiato nella variabile temporanea della

chiamata.

int main() {

int a, b=5;

a = f(b)+3;

temp z;

}

Per evitare la copia del risultato “int & f(int x)” z deve continuare ad esistere anche dopo la

funzione f (z non può essere dichiarato nella funzione... può essere passato come riferimento alla f).

Parametri const reference

es: inf f(const int & x) {…}

& → evita la copia

const → evita modifica da parte di f

Vantaggi:

– No side-effect sul parametro attuale (come per il passaggio per valore) il parametro passato

non può essere modificato.

– Efficienza

– No copia: evita problemi con oggetti difficili da copiare

Es: class razionale

class razionale

{ ...

razionale operator+ (const razionale & s) const {...}

razionale operator < (const razionale & s) const {...}

void stampa (ostream & dest) const {...}

}

ofstream & operator << (ostream & dest, const razionale & x) {...}

istream & operator >> (istream & sorg, razionale & x) {...}

n.b. es:

const razionale unmezzo (1,2);

...

c=a+unmezzo;

unmezzo.stampa(cout); // ½

– Il compilatore deve sapere che una funzione non modifica la const

– Tutte le funzioni invocate su un oggetto const devono essere funzioni const (la funzione

const non modifica l'oggetto di invocazione)

– Stessi problemi per gli oggetti dichiarati const &

es: ostream operator << (ostream & dest, const razionale & x) {

x.stampa(dest);

}

Invoca funzione propria di razionale su oggetto const.

Passaggio parametri – valori di default

Nella dichiarazione di una funzione (interna o esterna) è possibile dare un valore di inizializzazione

ad 1 o più parametri.

Es: int f(int x, int y=0) {…}

Nella chiamata di funzione è possibile omettere valore parametro attuale per parametro

inizializzato.

Nb: - valori di default per ultimi

- attribuiti alle funzioni overloaded

TIPO STRINGA string

Definizione (informale): sequenze di n caratteri consecutivi (n>=0)

es: "c1,c2,...cn"

" " = stringa vuota

operazioni :

- lunghezza stringa (attributi stringa)

- confronto tra stringhe ( ==, =!,< etc..)

- assegnamento tra stringhe ( r <---- s)

- selezione dell'i-esimo carattere

- lettura e scrittura di stringhe

- ricerca sottostringa

etc...

Implementazione stringhe

dobbiamo scegliere una rappresentazione dei dati, abbiamo due alternative :

- stringa come vettore: vettore statico (stringhe tipo C) o dinamico

- stringa come lista concatenata

implementazione in c++ : avviene tramite il costrutto CLASS e precisamente con una classe default

di nome STRING

#include <string>

es:

class string {

private:

int lungh;

//implementazione stringa,la rappresentazione di una struttura dati completa x la stringa

public :

//costruttori :

static const int npos= -1;

string () {

lung =0;

...}

string (char s[]) {

lungh =strlen(s); // a "lungh" assegno la lunghezza di s

...}

string (int n,char f) {

...

for(int i=0;izn;i++) { "assegna a i-esimo elem. F" }

//ora facciamo una funzione size

int size () {

return lungh;}

string operator = ( const string& r) {

...} // ottiene la copia di una stringa

string operator = ( const char r []) {

...}

bool operator == (const string& r) {...}

string operator + (const string& r) {...}

string operator + (char c) {...}

char operator [] ( int i) {...}

string substr (int p,int n){...}

int find (const string& r,int p) {

if("trovata") return "posizione iniziale di r "

else return npos;

} istream& operator >> (istream& sorg,string& s) {...}

ostream& operator << ( ostram& dest,const string & s) { ...}

istream getline (istream&sorg,string& s,char d=='\n') {...}

Uso nel main :

#include <string>

int main () {

string s; // ogg di classe string = stringa vuota

string r= "ciao"; // E' uguale a scrivere sting r("ciao"),"ciao" è un array di caratteri

//crea una stringa di nome r inizializzata con "ciao",l'attributo della stringa E' 4

string t(3,'a') // crea una stringa t e inizializza i primi 3 car. con la lettera a ( aaa )

Lunghezza stringa (size)

s.size() ---> lunchezza stringa s

es: string data = "22 maggio 2002" ;

cout <<data.size (); --- > 14

Assegnamento tra stringhe

s=r //ad s assegna la stringa r;

es: string r="ciao";

string s; //vuota

s = r; // s = ciao

es: string s;

s = "ciao"

//il "=" va ridefinito per degli array char,il primo operator usato su string nn va bene!

//infatti se si vede sopra gli "operator =" sono due!

Alcune operazioni:

=, ==,!=,<=,>= etc...

s==r ---> s.operator == (r)

Concatenazione:

s+r --> restituisce una nuova stringa concatenando r a s

es: string s1 = "ciao";

string s2 = s1 + "mondo"; --> s1.operator + ("mondo")

string s3 = s2 + '!';

cout<<s3;

//nb: string s="a";

//string s='a'; ---> errore!

Selezione singolo carattere:

s[i]

es: string s= "ciao";

cout<< s[1]; --> stampa i //s[1] verr‡ tradotta in : s.operator[] (1)

s[0] ='m'; //s[0] verr‡ tradotta in : s.operator[] (0)

cout <<s; --> miao

Sottostringa

s.substr (p,n) //restituisce la sottostringa r di s a partire da p lunga n

es: string data= "22 maggio 2002"

string mese;

mese= data.substr(3,6);

cout<<mese; ---> //stampa maggio

Ricerca sottostringa

s.find(r,p)

//cerca una sottostringa r all'interno di s a partire dalla posizione p

il risultato è un valore int che rappresenta la posizione iniziale dove E' stata trovata la stringa r,se

non la trova restituisce un valore speciale npos (string :: npos)

es: string frase="informatica";

int i;

i=frase.find("for",0) // cerco "for" a partire da 0

if( i==string :: npos) { cout<<"non trovata!"<<endl; }

else cout<<"trovata in posizione"<< i;

Lettura e scrittura stringhe

es << : string msg1= "immetti numero";

cout <<"ms1;

es: cin >> string nome,cognome;

cin >>nome;

cin>> cognome; ---> operator >> (cin,cognome)

//ignora spazi iniziali e termina al primo spazio o a capo

getline (f,s,d) // legge dallo stram di input f la stringa s;termina quando incontra il

carattere d o incontra end of file

es: string nomefile;

getline(cin,nomefile,'\n')

con stringhe di tipo c:

char nome.file[100];

cin.getline (nomefile,100,'\n');

Voglio passare da string a stringa di "tipo C" e viceversa

es: char c [10]="ciao";

string s = c; --> da cstring a string!

"nb :" per fare il contrario ( string --> cstring) usiamo una funzione della classe string: c_str

es: s.c_str () ---> r; // converte la stringa di tipo string nella stringa di "tipo c" r

"nb" : nome di un file E' una stringa "tipo c" ( = array di char)

es : string nomefile;

cin >> nomefile;

...

istrean f1;

f1.open (nomefile.c_str());

LISTA DI INTERI tipo di dato astratto

- valori: sequenza di n ( n >=0) numeri interi

es: ( i1,i2,...,in)

Operazioni :

- lunghezza della lista ( attributo)

- elemento di testa di una lista (attributo)

- elemento di cosa di una lista (attributo)

- inserisci/estrai testa

- inserisci/estrauÏi coda

- inserisci/estrai prima\dopo una certa informazione x

- concatenazione di due liste

...

nb: - no dimensione massima

- alta dinamicità

- no accesso tramite indice

Implementazione

Lista: dobbiamo decidere la rappresentazione dei dati, abbiamo due scelte:

- con un vettore (array) dinamico

- lista concatenata

Implementazione liste concatenate con class

class lista

Sol. 1) tipo elem con struct

struct elem {

int info;

elem*punt;};

class lista {

private : elem* l;

public :

lista (){

l=NULL;

}

void inseriscitesta (int x) {

elem *t;

t= new elem;

t->info =x;

t-put=l;

l=t:

return;}

Uso int main (){

lista l1; //crea lista l1 vuota

lista l2; // creo lista l2 vuota

ll.inseriscitesta (5);

Class lista

struct elem {

int info;

elem*punt;}

class lista {

private :

elem* l;

public :

lista ()

{l=NULL;}

void inseriscitesta (int x) {

elem*t;

t=new elem:

t->info=x;

t->int=l;

l=t;

return;}

void stampa (ostream & dest) {

elem * t =l;

while ( t!= NULL) {

dest<<t->info<<" ";

t= t->punt;

}

return;}

...

};

...

int main (){

lista l1;int x,y;

cout <<"dai un numero"<<endl;

cin >>x;

l1.inseriscitesta(x); // mette x in testa di l1

cout<<"dai un numero"<<endl;

cin>>y;

l1.inseriscitesta(y); //mette y in testa ;alloca un nuovo elemento e lo attacca alla lista

lista l2;

l2.inseriscitesta(y); //crea una nuova lista concatenata e mette in testa y

...

}

Lista senza class

struct elem {..};

void inseriscitesta ( elem *& l,int x) {

elem *t;

t= new elem;

t-> info=x;

t-> punt =l;

l=t;

return; }

void stampa ( ostream& dest, elem * l) {

while ( l!=NULL) {

l = l->punt;}

...

int main () {

elem + l1 = NULL;

int x,y;

cout <<"...";

cin >> x;

inseriscitesta (l1,x) ;

cout <<"...";

cin >>y;

inseriscitesta (l1,y);

Variante con typedef

typedef int t_info; // t_info E' di tipo int

struct elem {

t_info info;

elem * punt;};

class lista {

...

void inseriscitesta ( t_info x) { => maggior modificabilità del programma

...}

...

};

Variante 2 senza struct

class elem {

private : int info;

elem * punt;

public :

elem () {

punt =NULL;}

elem (int x,elem*s=NULL) {

info= x;

punt = s;

int get_info () {

return info; } //SELETTORI (getter)

elem * get punt () {

return punt; }

void put_info (int x)

info=x;}

void put_punt( elem *s) {

punt= s;}

void stampa (ostream& dest)

{dest <<info;}

}

class lista {

private : elem *l;

public :

lista () { l=NULL;}

void inseriscitesta(int x) {

elem *t;

t=new elem;

void inseriscitesta (int x) {

elem *t;

t=new elem;}

t->put->info (x);

t->put_punt(l);

l=t;

return;}

void stampa (ostream & dest){

...

dest<<t->get_info()<<" ";}

Distruttori

struct elem {

int info;

elem*punt;}

class lista {

private:

elem* l;

public:

lista ()

{l=NULL;}

Distruzione di un oggetto

void f (int x) {

razionale A;

.....

return; → distruzione di A,invoca il distruttore della classe razionale e rilascia la

memoria allocata per A

}

es: class razionale {

...

public :

...

~razionale () {

...}

...}

ogni classe ha distruttore predefinito (non fa nulla) con corpo vuoto {} il distruttore di qualsiasi

classe può essere ridefinito quando voglio.

Es: class razionale {

...

public :

...

~razionale ()

{ cout <<"sto morendo"<<endl;}

...

}

es: clase lista

void f (int x) {

lista l1;

....

l1.inseriscilista (x);

l1.inseriscilista (y);

return; ---> dealloca l1 ma NON dealloca elem. della lista ==> GARBAGE

}

int main () {

f(3);

soluzione: ridefinire il distruttore della classe lista che rimpiazza quello di default

~lista () {

while (l!=NULL){

elem t;

t= l-->punt;

delete l;

l=t;}

}

GESTIONE MEMORIA DI UN PROGRAMMA

3 tipo diversi di allocazione della memoria :

- allocazione statica → usata in particolare per variabili dichiarate globalmente del programma

- allocazione automatica → allocazione dati locali a un blocco. Per “blocco di dati locali” si

intende variabili locali, parametri formali, variabili temporanee per risultati intermedi, indirizzo di

ritorno, questi sono anche chiamati RECORD DI ATTIVAZIONE DELLA FUNZIONE

- allocazione dinamica

es – allocazione statica:

#include <iostream>

int A[10];

int x; → due variabili globali allocate staticamente (memoria allocata a compile time

e deallocata alla fine del programma) il tempo di vita è l'intero programma

int main () {...

...}

es – allocazione automatica :

int f (int z) {

int x=0; ===> record d attivazione attivato automaticamente quando si entra nel

blocco (es quando si fa la chiamata della funzione,li allora viene allocata la memoria) il record

d'attivazione viene deallocato quando si esce dal blocco,quindi il tempo di vita di variabili

automatiche è il blocco, ovvero il tempo di esecuzione del blocco in cui la variabile appare

return x+1;

}

int main () {

int A;

A=f(7); → r.a. allocati in area di memoria gestita come pila (stack)

...}

es2 - allocazione automatica :

int g (int y) {

int x=1;

…}

int f (int z) {

int x=0;

x=g(3);

... → "catena dinamica delle chiamate di funzioni"

}

int main () {

int A=7;

cout <<f(A);

...

}

Pila dei record di attivazione (dimensione non nota a priori)

es - allocazione dinamica (funzioni ricorsive)

fatt (int x)

{ ...

return fatt (x-1)*x;

}

int main () {

fatt (4);

Se la pila E' piena si ha un errore runtime "stack overflow"

es2 - allocazione dinamica (creazione tramite new)

int main() {

int* x; → allocata automaticamente

x=new int(1); → allocazione dinamica

- distruzione esplicita tramite delete

- tempo di vita variabile dinamica = intervallo di tempo tra new e delete

- allocate in heap → aree di memoria distinte tra aree libere e occupate (tramite tabella o liste)

ovviamente quelle libere sono quelle usate dalla new

es3: int A; → variabile statica

int f ()

{ int *x; → variabile automatica

x=new int(1); → variabile dinamica

...

}

int main () {

int z=5; → variabile automatica

f();

}

Variabile dinamica non deallocata al termine della funzione f ( ma il puntatore x è deallocato) → si

forma della spazzatura ("garbage") → usare delete

GESTIONE ECCEZIONI

Il problema è gestire in modo opportuno situazioni anomale che si verificano durante

l'ESECUZIONE del programma (gestire gli errori a runtime).

Soluzione SENZA gestione eccezioni: l'errore in una funzione viene tipicamente gestito nella

funzione con (ad es) un messaggio d'errore

Anteprima
Vedrai una selezione di 14 pagine su 63
Programmazione B - Tutta la teoria Pag. 1 Programmazione B - Tutta la teoria Pag. 2
Anteprima di 14 pagg. su 63.
Scarica il documento per vederlo tutto.
Programmazione B - Tutta la teoria Pag. 6
Anteprima di 14 pagg. su 63.
Scarica il documento per vederlo tutto.
Programmazione B - Tutta la teoria Pag. 11
Anteprima di 14 pagg. su 63.
Scarica il documento per vederlo tutto.
Programmazione B - Tutta la teoria Pag. 16
Anteprima di 14 pagg. su 63.
Scarica il documento per vederlo tutto.
Programmazione B - Tutta la teoria Pag. 21
Anteprima di 14 pagg. su 63.
Scarica il documento per vederlo tutto.
Programmazione B - Tutta la teoria Pag. 26
Anteprima di 14 pagg. su 63.
Scarica il documento per vederlo tutto.
Programmazione B - Tutta la teoria Pag. 31
Anteprima di 14 pagg. su 63.
Scarica il documento per vederlo tutto.
Programmazione B - Tutta la teoria Pag. 36
Anteprima di 14 pagg. su 63.
Scarica il documento per vederlo tutto.
Programmazione B - Tutta la teoria Pag. 41
Anteprima di 14 pagg. su 63.
Scarica il documento per vederlo tutto.
Programmazione B - Tutta la teoria Pag. 46
Anteprima di 14 pagg. su 63.
Scarica il documento per vederlo tutto.
Programmazione B - Tutta la teoria Pag. 51
Anteprima di 14 pagg. su 63.
Scarica il documento per vederlo tutto.
Programmazione B - Tutta la teoria Pag. 56
Anteprima di 14 pagg. su 63.
Scarica il documento per vederlo tutto.
Programmazione B - Tutta la teoria Pag. 61
1 su 63
D/illustrazione/soddisfatti o rimborsati
Acquista con carta o PayPal
Scarica i documenti tutte le volte che vuoi
Dettagli
SSD
Scienze matematiche e informatiche INF/01 Informatica

I contenuti di questa pagina costituiscono rielaborazioni personali del Publisher Mr.Al di informazioni apprese con la frequenza delle lezioni di Fondamenti di programmazione e studio autonomo di eventuali libri di riferimento in preparazione dell'esame finale o della tesi. Non devono intendersi come materiale ufficiale dell'università Università degli Studi di Parma o del prof Scienze matematiche Prof.
Appunti correlati Invia appunti e guadagna

Domande e risposte

Hai bisogno di aiuto?
Chiedi alla community