Teoria - Programmazione BStream come parametri di funzione
Funzione copia file
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) {
dest << n << "/" << d;
return;
}
void leggi(istream &sorg) {
sorg >> num >> sep >> den;
...
}
};
int main() {
razionale a(2,3);
...
a.stampa(cout);
ofstream f_out;
f_out.open("risultati.txt");
a.stampa(f_out);
}
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;
Parametri const reference
es: int 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
Esempio: Classe 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
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
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 () {lungh =0; ...}
string (char s[]) {lungh =strlen(s); // a "lungh" assegno la lunghezza di s ...}
string (int n,char f) {... for(int i=0; i<n; 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 è 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() ---> lunghezza 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 è 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 stream 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');
Conversione tra string e C-string
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 è una stringa "tipo c" ( = array di char)
es : string nomefile;
cin >> nomefile;
...
istream 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/estrai 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
l1.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);
cout<<"dai un numero"<<endl;
cin>>y;
l1.inseriscitesta(y);
lista l2;
l2.inseriscitesta(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 è 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; }
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
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 () {
......
}
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)
Allocazione dinamica (funzioni ricorsive)
fatt (int x){ ...
return fatt (x-1)*x;
}
int main () {
fatt (4);
Se la pila è piena si ha un errore runtime "stack overflow"
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
Scarica il documento per vederlo tutto.
Scarica il documento per vederlo tutto.
Scarica il documento per vederlo tutto.
Scarica il documento per vederlo tutto.
Scarica il documento per vederlo tutto.
Scarica il documento per vederlo tutto.
Scarica il documento per vederlo tutto.
Scarica il documento per vederlo tutto.
Scarica il documento per vederlo tutto.
Scarica il documento per vederlo tutto.
Scarica il documento per vederlo tutto.
Scarica il documento per vederlo tutto.
-
Programmazione web
-
Appunti Linguaggi di programmazione
-
Programmazione e controllo Appunti
-
Teoria di Informatica e Programmazione