Molt
: costruttori explicit
, coda
cout
: i manipolatori #include <stdio.h> struct pt2 { double x, y; } p1 = { 3. , 5. }; struct pt2 somma(struct pt2 a, struct pt2 b); struct pt2 scala(double, struct pt2); struct pt2 makept2(double , double); int printpt2(struct pt2); int main( ) { struct pt2 p2 = { 7., -1. }, p3; printf("p1: "); printpt2(p1); printf("p2: "); printpt2(p2); printf("p2 - p1: "); printpt2(somma(p2,scala(-1.,p1))); printf("punto medio: "); printpt2(scala(.5,somma(p2,p1))); p3 = makept2( 2, 5 ); printf("p3: "); printpt2(p3); printf("baricentro: "); printpt2(scala(1./3, somma(p3,somma(p2,p1)))); return 0; } struct pt2 somma(struct pt2 a, struct pt2 b) { struct pt2 loc; loc.x = a.x + b.x; loc.y = a.y + b.y; return loc; } struct pt2 scala(double k, struct pt2 a) { struct pt2 loc; loc.x = k * a.x; loc.y = k * a.y; return loc; } struct pt2 makept2( double x, double y ) { struct pt2 loc; loc.x = x; loc.y = y; return loc; } int printpt2( struct pt2 a ) { return printf("(% 4.2lf,% 4.2lf)\n", a.x,a.y); }Partendo dal codice che precede, definire il tipo
struct Tria
, con
tre campi, A
, B
e C
, per i vertici del
triangolo.mingraf.cpp
,
graph.h
e graph.cpp
.mingraf
./* mingraf.cpp */
#include <stdio.h> #include <math.h> #include <process.h> #include "graph.h" // prototipi da graph.cpp /* qui vanno incorporate le definizioni/dichiarazioni del programma precedente */ // -------------------- programma principale --------------- int main() { int xp=5,yp=5, xs=500,ys=500; CreaGraph( "GraphWindow", xp, yp, xs, ys ); // finestra quadrata di 500x500 pixel SetCoord(0,10,0,10); // coordinate virtuali [0,10]x[0,10] printf("spostare la finestra lontano dalla finestra grafica\n"); fflush(stdout); getchar(); MyMove(2,2); MyDraw(8,2); MyDraw(8,8); MyDraw(2, 8); MyDraw( 2, 2); getchar(); ClearWin(); SetLineCol( RGB(0,255,255) ); MyMove(2,2); MyDraw(8,2); MyDraw(2, 8); MyDraw( 2, 2); MyCirc(5,5,3); getchar(); Cancella(); MyMove(2,2); MyDraw(8,2); MyDraw(2, 8); MyDraw( 2, 2); getchar(); Scrivi(); MyMove(2,2); MyDraw(8,2); MyDraw(2, 8); MyDraw( 2, 2); getchar(); return 0; }
#include <stdio.h> int x=5, y=10; void prova( ); /* prototipo */ int main( ) { printf("main(1): %5d %5d\n", x, y); prova(); printf("main(2): %5d %5d\n", x, y); { double x=3.14, y=6.28; printf("main(3): %5.2lf %5.2lf\n",x,y); } printf("main(4): %5d %5d\n", x, y); } void prova( ) { int y; y = x*x; x=15; printf("prova: %5d %5d\n", x, y); }
#include <stdio.h> void cambia( int a ) { printf("cambia: a = %d\n", a); a=345; printf("cambia: a = %d\n", a); } int main( ) { int a = 1; printf("main: a = %d\n", a); cambia(a); printf("main: a = %d\n", a); return 0; }
#include <stdio.h> void doppio( double x ) { x *= 2; } void doppioV( double x[], int n ) { while (n-- > 0) x[n] *= 2; } void prtvecd( double x[], int n ) { int i; for (i=0; i < n; i++) printf("%5.2lf ", x[i]); printf("\n"); } int main() { double a[]={ 10., 20., 30, 40. }; int n=sizeof(a)/sizeof(a[0]); prtvecd(a, n); doppio(a[0]); prtvecd(a, n); doppioV(a, n); prtvecd(a, n); return 0; }
#include <stdio.h> #define NMAX 1000 int num[NMAX]; /* globale, deve essere vista da tutte le funzioni */ int is_prime(int n); /* deve restituire 0 se n non e' primo, 1 altrimenti */ void set_composed( int n ); /* deve registrare che n e' un numero composto */ int main() { int i,j,k; int p; for (i=0;i<NMAX; i++) num[i]=i; /* p nasce 2 */ for (p=2; p<NMAX; p++) /* si puo' fare di meglio */ { /* se p non e' primo, si salta: * scrivere il test e usare * continue * per passare al valore ancora successivo * scelta iniziale suggerita: * p non e' primo <== (num[p] == 0) */ ....; /* * p e' primo: quindi i numeri p+p, p+p+p, ... * sono composti * usare la funzione set_composed per registrare la cosa */ ....; } for (i=2,j=0; i<NMAX; i++) { if (is_prime(i)) { printf("%5d",i); /* qui j viene incrementato e ridotto modulo 12 */ j = ...; /* usare l'operatore ternario per stampare * ' ' oppure '\n' a seconda che j!=0 o j==0 */ printf("%c", ... ); } } return 0; }Dopo aver preso visione del codice sugli operatori bitwise, usare un vettore di
NELEM
interi per rappresentare gli
interi da 0 a NMAX
, come segue
#define NELEM 1000 #define NMAX (32*NELEM) int num[NELEM];La funzione
init_sieve
deve inizializzare gli elementi del vettore
num
con il numero int
i cui bit sono tutti 1
(cioè 0xFFFFFFFF = -1L
); la funzione set_composed(int n)
dovrà avere all'incirca il seguente aspetto:
void set_composed(int n)
{
int nh = n/32;
int nl = n%32;
/*
* devo mettere in num[nh] un intero in cui il bit in posizione nl
* e' 0 mentre tutti gli altri restano invariati
* si puo' realizzare facendo l'and bit a bit (operatore &)
* con un int che ha 1 dappertutto tranne che in posizione nl
* 1L << nl ha un pattern esattamente complementare
* negandolo bit a bit si ottiene il numero int
richiesto
* ~( 1L << nl )
*/
num[nh] = .... ;
}
analoghe modifiche servono per is_prime
.
#include <stdio.h> void set_bit(int *n, int pos) { int mask = 1<<pos; *n = ( *n | mask ); /* che possiamo scrivere come : *n |= (1L << pos); */ } void clear_bit(int *n, int pos) { *n &= ~( 1L << pos ); } int is_set(int n, int pos) { return (n & ( 1L << pos )) != 0; } void stampa_bin(int n) { int i; printf("%5d (%08x) --->", n, n); for (i=31; i>=0; --i) printf("%c",(is_set(n, i))?'1':'0'); printf("\n"); } int main() { int j, pos; int n; n = 1+8+512+2048; stampa_bin(n); for (j=0;j<3;j++) { printf("accendere il bit in posizione pos [0-31] = "); scanf("%d",&pos); set_bit(&n,pos); stampa_bin(n); } for (j=0;j<3;j++) { printf("spegnere il bit in posizione pos [0-31] = "); scanf("%d",&pos); clear_bit(&n,pos); stampa_bin(n); } /* lo xor con 0xFFFFFFFF inverte tutti i bit */ n ^= 0xFFFFFFFF; stampa_bin(n); return 0; }
#include <stdio.h> void copia( char *dst, char *src ) { while ( *dst++ = *src++ ) ; } int main() { char buf1[]="Hallo!", buf2[20]; copia(buf2, buf1); printf("%s\n", buf2); return 0; }Esercizio: modificare l'esempio precedente per leggere da tastiera la stringa con l'istruzione
scanf("%s", buf1);poi calcolarne e stamparne la lunghezza; convertirla in caratteri maiuscoli (se c è compreso fra 'a' e 'z' gli si deve sommare 'A' - 'a').
st
di tipo char *
, inverte la stringa che inizia in posizione st
sul posto e restituisce come valore il parametero in ingresso. Il codice
char msg[] = "Hallo!"; printf("reverse(%s) = ", msg); printf("%s\n", reverse(msg));deve stampare
reverse(Hallo!) = !ollaH
#include <stdio.h> #include <stdlib.h> struct Nodo { int i; struct Nodo *x; } *lista = NULL; struct Nodo *add( struct Nodo *p, int i ) { struct Nodo *nuovo; nuovo = (struct Nodo *)malloc( sizeof(struct Nodo) ); nuovo->i = i; nuovo->x = p; return nuovo; } void prtlist( struct Nodo *p ) { for ( ; p; p = p->x ) printf("%5d\n", p->i); } void killist( struct Nodo *p ) { if (p != NULL) { killist( p->x ); free( p ); } } int main() { int i; for ( ; ; ) { printf("i: "); scanf("%d", &i); lista = add( lista, i ); if (i == 0) break; } prtlist( lista ); killist( lista ); return 0; }
1. | modificare il programma in modo che lo 0 non venga inserito nella lista. |
2. | Se la funzione malloc fallisce e restituisce NULL , la
linea successiva produrrà un errore. Modificare la funzione add
in modo che i nuovi numeri inseriti vengano semplicemente ignorati.Per verifica, definite una funzione void *my_malloc(unsigned int n)
e usatela dentro add . my_malloc deve tenere conto in
una variabile static int di quanta memoria ha già ottenuto
dal sistema, e restituire NULL se ha superato la soglia di 50
bytes, oppure restituire il valore di malloc( n ) .
|
3. | Salvare il file con un nuovo nome, e modificarlo in modo che prtlist
stampi i numeri nell'ordine in cui sono stati inseriti. Si deve modificare la
sola funzione add , che dovrà fare cose un po' diverse nel
caso che sia p == NULL oppure p != NULL .
|
4. | Partendo dalla modifica precedente, scrivere un programma che inserisce ogni nuovo numero nel posto che gli compete rispetto all'ordinamento crescente. |
#include <stdio.h> #include <math.h> typedef double dfund( double ); typedef double metodo( dfund *, double a, double b ); double Psin ( double x ) { return -cos(x); } double UnoSuX ( double x ) { return 1/x; } struct TestFunz { char *nome; dfund *fun, *Pfun; } EsempiFunz[] = { {"sin", sin, Psin}, {"cos", cos, sin}, {"1/x", UnoSuX, log}, }; int nFunz = sizeof(EsempiFunz)/sizeof(EsempiFunz[0]); double PuntoMedio( dfund *f, double a, double b ) { return (b-a) * (*f)((a+b)/2); } double trapezi( dfund *f, double a, double b ) { return (b-a) * (f(a)+f(b))/2; // equiv. a (*f)(a)+(*f)(b) } struct Metodo { char *nome; metodo *met; } metodi[] = { // {"primo estremo", eulSum}, // da implementare // sul rettangolo [a,b] restituisce f(a)*(b-a) {"metodo del punto medio", PuntoMedio}, {"metodo dei trapezi", trapezi}, // Simpson e Simpson 3/8: da wikipedia, numerical integration }; int nMetodi = sizeof(metodi)/sizeof(metodi[0]); double Integra (dfund *f, double a, double b, metodo *met, int n) { // calcola l'integrale della funzione il cui indirizzo è // passato nel primo parametro suddividendo l'intervallo // [a,b] in n sottointervalli, invocando il metodo passato // nel parametro met (a cui si passano come parametri la funzione // e gli estremi del sottointervallo) e accumulando il risultato int i; double ris = 0, h = b-a; for (i=0; i < n; i++) ris += (*met)(f, a + ((double) i)*h/n, a + ((double) (i+1))*h/n); return ris; } int main() { int i, j, nBisez, n, nF; double a, b, vero, calcolato; metodo *met; dfund *f, *pf; printf("funzioni: \n"); for (i = 0; i<nFunz; i++) printf("%2d %s\n", i, EsempiFunz[i].nome); printf("Scegliere: "); scanf("%i", &nF); f = EsempiFunz[nF].fun; pf = EsempiFunz[nF].Pfun; printf("assegna estremi intervallo: "); scanf("%lf %lf",&a,&b); vero = pf(b)-pf(a); printf("assegna nBisez: "); scanf("%d", &nBisez); for (i=0; i < nBisez ; i++) { n = pow(2,i); printf("%2d %le %le\n", i, fabs(Integra (f, a, b, metodi[0].met, n) - vero), fabs(Integra (f, a, b, metodi[1].met, n) - vero) ); } return 0; }
h =
h0*pow(2.,-i)
con i=0,...,8
e stampa in formato
esponenziale lo scarto dalla derivata prima calcolata esattamente.
#include <stdio.h> #include <math.h> double fun(double x) { return sin(x); } double dfun(double x) { return cos(x); } double diffa( double (*f)( double), double x, double h ) { return (f(x+h)-f(x))/h; } double diffi( double (*f)( double), double x, double h ) { return (f(x)-f(x-h))/h; } double diffs( double (*f)( double), double x, double h ) { return (f(x+h)-f(x-h))/h/2; } int main() { int i,j; double x, h, h0 = 0.01, Dfx; x = 1; Dfx = dfun(x); for (i=0; i < 8 ; i++) { h = h0*pow(2.,-i); printf("%2d %le %le %le\n", i, fabs(diffa(fun,x,h)-Dfx), fabs(diffi(fun,x,h)-Dfx), fabs(diffs(fun,x,h)-Dfx)); } return 0; }Compilarlo e verificare che le due prime approssimazioni sono del primo ordine nel passo e che quella alle differenze centrate è del secondo ordine.
x
di f(x+2*h)
e
di f(x+h)
eliminare il termine in f''(x)
e ricavarne
una approssimazione di ordine due di f'(x)
in termini di f(x+2*h)
,
f(x+h)
e f(x)
, implementarla come una funzione diffa2
e modificare il programma per mostrare il comportamento della approssimazione
ottenuta.
f(x)
, f(x+h)
, f(x+2h)
che portano ad una approssimazione di f'(x)
di ordine superiore al
primo con differenze finite in avanti, vogliamo provare a determinare i pesi
per avere approssimazioni di ordine superiore.
Per ottenere una
approssimazione di f'(x)
che sia O(h^k)
con
differenze finite in avanti prendo gli sviluppi di Taylor in x
di
f(x+h)
, f(x+2h)
, ... f(x+(k+1)h)
, trascuro
gli o(h^{k+1})
e scrivo il tutto come
f(x+ h) - f(x) = A_1 + A_2 + A_3 + ... + A_{k+1} f(x+ 2h) - f(x) = 2 A_1 + 2^2 A_2 + 2^3 A_3 + ... + 2^{k+1} A_{k+1} f(x+ 3h) - f(x) = 3 A_1 + 3^2 A_2 + 3^3 A_3 + ... + 3^{k+1} A_{k+1} ...................................................................................... f(x+(k+1)h) - f(x) = (k+1)A_1 + (k+1) A_2 + (k+1)^3 A_3 + ... + (k+1)^{k+1} A_{k+1}dove le
A_i = h^i D^i f(x)/(i!)
sono da.le incognite del sistema.
Risolvo il sistema; dividendo per h
l'espressione per A_1
ottengo l'approssimazione di f'(x)
all'ordine k
. 1 1 1 ... 1 A_1 2 2^2 2^3 ... 2^(k+1) A-2 3 3^2 3^3 ... 3^(k+1) x A_3 = ... ... ... ... ... ... (k+1) (k+1)^2 (k+1)^3 ... (k+1)^(k+1) A_{k+1} -1 1 0 0 ... 0 f_0 -1 0 1 0 ... 0 f_1 = -1 0 0 1 ... 0 x f_2 ... ... ... ... ... ... ... -1 0 0 0 ... 1 f_k f_{k+1}con
f_j = f(x + j*h)
; voglio diagonalizzare la prima matrice
eseguendo solo operazioni sulle righe, eseguite su numeri interi per avere risultati esatti,
e ripetendo le stesse operazioni sulle righe della matrice a secondo membro.
Per fare questo accorpo le due matrici in una con k+1
righe e k+1 + k+2
colonne.
1 1 1 ... 1 -1 1 0 0 ... 0 2 2^2 2^3 ... 2^(k+1) -1 0 1 0 ... 0 3 3^2 3^3 ... 3^(k+1) -1 0 0 1 ... 0 ... ... ... ... ... ... ... ... ... ... ... (k+1) (k+1)^2 (k+1)^3 ... (k+1)^(k+1) -1 0 0 0 ... 1Usando la prima riga eliminerò tutte le componenti della prima colonna, tranne la prima; usando la
k
-ma riga metterò a 0
tutte le componenti della k
-ma colonna tranne quella di posto k
.M[i][j]
occorre che M[i]
sia un array, e dunque che M
sia un
vettore di indirizzi di array, come si vede nella funzione allocmat
.#include <stdio.h> #include <stdlib.h> #include <math.h> typedef struct _MyIntMat { int nr, nc; int **mat; } MyIntMat; MyIntMat *allocmat( int nr, int nc ) { int i,j; MyIntMat *locale; int **mat; locale = (MyIntMat *)malloc(sizeof(MyIntMat)); if (locale == NULL) return NULL; locale->nc = nc; locale->nr = nr; mat = (int **)malloc(nr * sizeof(int *)); if (mat == NULL) { free(locale); return NULL; } locale->mat = mat; for (i=0; i < nr; i++) { mat[i] = (int *)malloc(nc * sizeof(int)); if ( mat[i] == NULL ) { /* se l'allocazione della i-ma riga fallisce, la funzione termina restituendo NULL. Aggiungere le istruzioni necessarie per liberare (con free) tutta la memoria allocata fino a questo punto! */ // ... OMISSIS ... return NULL; } } // inizializzo a zero for (i=0; i < nr; i++) { for (j=0; j < nc; j++) { mat[i][j] = 0; } } return locale; } void freemat( MyIntMat *mm ) { int i, nr = mm->nr; for ( i=0; i < nr; i++) free(mm->mat[i]); free(mm->mat); free(mm); } void printmat( MyIntMat *mm ) { int i, j, nc = mm->nc, nr = mm->nr; int **mat = mm->mat; for ( i=0; i < nr; i++) { for ( j=0; j < nc; j++) { printf( "%8d ", mat[i][j]); } printf("\n"); } printf("\n"); } void fillMat( MyIntMat *mm ) { int i,j, nr = mm->nr, nc = mm->nc; int **mat = mm->mat; /* la 1.a sottomatrice ha per colonne le potenze successive di 1, 2, 3, ...; la colonna immediatamente seguente contiene -1; la 2.a sottomatrice e' la matrice identica. */ // ... OMISSIS ...
} void diagBlock( MyIntMat *mm ) { int i, nc = mm->nc, nr = mm->nr; int **mat = mm->mat; int k, l; /* Nel ciclo di eliminazione della colonna k-ma, si sostituisca la riga l-ma con la differenza fra la riga l-ma moltiplicata per il numero fact_l e la riga k-ma moltiplicata per il numero fact_k scelti in modo che la nuova riga abbia 0 in posizione k-ma e abbia i coefficienti piu' piccoli possibile. A tale scopo, puo' essere utile scrivere una funzione int MCD( int a, int b ) che restituisce il massimo comun divisore fra |a| e |b| con l'algoritmo delle divisioni successive */ // ... OMISSIS ... } int main() { int k; MyIntMat *A; printf("assegna k: "); scanf("%d", &k); A = allocmat( k+1, k+1 + k+2 ); fillMat( A ); printmat( A ); diagBlock( A ); printmat( A ); getchar(); return 0; }Potrete verificare che il risultato è corretto aggiungendo al programma dell'esercizio precedente le funzioni
diffa3
, diffa4
,
... che usano i pesi appena calcolati.
#include <iostream> using namespace std; /* overloading di funzioni: in C++ sono ammesse piu' funzioni con lo stesso nome che si distinguono per numero e/o tipo dei parametri (si dice che la funzione add che segue e' OVERLOADED) parametri opzionali: nella lista dei parametri qualcuno puo' essere dichiarato opzionale e prende se manca il valore indicato dopo il segno = i parametri che hanno un valore di default DEVONO SEGUIRE tutti quelli NORMALI */ int add(int x, int y) { return x+y; } int add(int x, int y, int z) { return x+y+z; } int mul(int x, int y, int z=1) { return x*y*z; } int main() { cout << "3+5 = "<< add(3,5) << ", 3+5+7 = " << add(3,5,7) << '\n'; cout << "3*5 = "<< mul(3,5) << ", 3*5*7 = " << mul(3,5,7) << '\n'; return 0; }
#include <iostream> using namespace std; void incr(int &a) { a++; } void decr(int &a) { a--; } int main() { int i,j; int &a=i, &b=j; int &c; // segnala errore: references must be initialized cout << "assegna a: "; cin >> a; cout << " ora i vale " << i << "\n"; cout << "assegna b: "; cin >> b; cout << " ora j vale " << j << "\n"; incr(i); decr(j); cout << " ora i vale " << i << "\n"; cout << " ora j vale " << j << "\n"; return 0; }
#include <iostream> using namespace std; // dichiarazione della classe Molt (interfaccia) // // scommentare nelle varie combinazioni le due #define seguenti // #define NO_COPY_CTOR // #define NO_ASSIGNMENT // class Molt { private: int factor, numvolte; static int totvolte; #if defined(NO_COPY_CTOR) Molt(const Molt &); // privato, non implementato #endif #if defined(NO_ASSIGNMENT) Molt& operator=(const Molt &); // privato, non implementato #endif public: Molt(int n); int by( int x ); int numVolte( ); static int totVolte( ); }; int applica(Molt m, int i) { return m.by(i); } // dichiarazione della classe Molt (implementazione) int Molt::totvolte = 0; Molt::Molt(int n) { factor = n; numvolte = 0; }; int Molt::numVolte( ) { return numvolte; }; int Molt::totVolte( ) { return totvolte; }; int Molt::by( int x ) { numvolte++; totvolte++; return x*factor; }; int main( ) { Molt r = Molt(3); Molt s(5); // Molt s(Molt(5)); cout << "r.by(11) = " << r.by(11); cout << ", r.by(13) = " << r.by(13); cout << ", s.by(17) = " << s.by(17) << ".\n"; cout << "r.by chiamata " << r.numVolte() << " volte,\n"; cout << "s.by chiamata " << s.numVolte() << " volte.\n"; cout << "by chiamata " << r.totVolte() << " volte.\n"; cout << "by chiamata " << s.totVolte() << " volte.\n"; cout << "s.by(3) = " << s.by(3); cout << ", s.by(5) = " << s.by(5); cout << ", s.by(7) = " << s.by(7) << ".\n"; cout << "r.by chiamata " << r.numVolte() << " volte,\n"; cout << "s.by chiamata " << s.numVolte() << " volte.\n"; cout << "by chiamata " << r.totVolte() << " volte.\n"; cout << "by chiamata " << s.totVolte() << " volte.\n"; r = s; cout << "applica(r,10) = " << applica(r,10) << " . \n"; return 0; }Compilare, eseguire; poi, scommentare a turno le due linee
// #define NO_COPY_CTOR // #define NO_ASSIGNMENTall'inizio del file e osservare gli errori di compilazione che il compilatore segnala.
Molt
: costruttori explicit
explicit
controlla questo comportamento.
#include <iostream> using namespace std; // togliendo il commento dalla linea successiva // il compilatore non puo' piu' fornire // una conversione automatica int --> Molt // e quindi fallisce la compilazione // della prima invocazione di applica #define EXPLICIT_CTOR // dichiarazione della classe Molt (interfaccia) class Molt { private: int factor; public: #if defined( EXPLICIT_CTOR ) explicit #endif Molt(int n=1); int by( int x ); }; // dichiarazione della classe Molt (implementazione) Molt::Molt(int n) : factor(n), numvolte(0) { }; int Molt::by( int x ) { return x*factor; }; int applica( Molt m, int n ) { return m.by(n); } int applica1( Molt& m, int n ) { return m.by(n); } int main( ) { int i; // la seguente linea di codice, se puo', // usa il costruttore per convertire 5 in Molt(5) // se e' definito il simbolo EXPLICIT_CTOR // il costruttore e' dichiarato esplicito // e la conversione automatica non viene accettata cout << applica( 5, 13 ) << endl; // cout << applica( Molt(5), 13 ) << endl; // scommentate la linea seguente per vedere l'errore // cout << applica1( 5, 13 ) << endl; cout << applica1( Molt(7), 13 ) << endl; cin >> i; return 0; }Altre sorprese: ordine di costruzione.
C
che ha come variabili membro
oggetti di classe C_1
, C_2
,... , C_k
è
possibile invocare i costruttori di tali variabili per inizializzarle con la
sintassi
class C { ... C_1 a; C_2 b; ... C_k z; public: C( ... ) : a( expr_1 ), b( expr_2 ), ... z( expr_k ) { ... } ... };Se la classe a cui appartiene la variabile membro non ha un costruttore di default, senza parametri, questo è in realtà l'unico modo per inizializzare tale variabile.
expr_1
, expr_2
, ..., expr_k
il valore di qualche variabile
membro già inizializzata.ClassA
quanto ClassB
definiscono oggetti che vengono inizializzati con un parametro intero, e hanno
una sola funzione membro, Show()
, che stampa il numero, il suo
quadrato e il suo cubo; questi valori vengono in entrambi i casi calcolati una
volta sola, alla costruzione dell'oggetto.#include <iostream> using namespace std; class ClassA { public: ClassA(int i=1) : x(i), s(x*x), c(x*x*x) {}; void Show( ) const { cout << "\n" << x << "\t" << s << "\t" << c << endl; } private: int x, s, c; }; class ClassB { public: ClassB(int i=1) : x(i), s(x*x), c(x*x*x) {}; void Show( ) const { cout << "\n" << x << "\t" << s << "\t" << c << endl; } private: int c, x, s; }; int main() { int i; ClassA x(2); ClassB y(2); x.Show(); y.Show(); cin >> i; return 0; }
#include <iostream> using namespace std; int i=3; int j(i); // l'inizializzazione e' un "costruttore di copia" class ClassA { private: int id; static int serial; public: ClassA() : id(++serial) { cout << "\n creo ClasseA: id = " << id << "\n"; } ClassA(const ClassA &orig) : id(orig.id) { cout << "\n clono ClasseA: id = " << id << "\n"; } ~ClassA() { cout << "\n distruggo ClasseA: id = " << id << "\n"; } ClassA& operator=(const ClassA &orig) { cout << "\n assegnamento:\t" << "copio ClasseA: id = " << orig.id << "\n"; id = orig.id; return *this; } }; int ClassA::serial = 0; class Scatola { private: int id; static int serial; ClassA pluto; public: Scatola() : id(++serial) { cout << "costruisco scatola n." << id << ", costruttore di default\n";}; Scatola(const ClassA &p) : id(++serial), pluto(p) { cout << "costruisco scatola n." << id << "\n"; }; }; int Scatola::serial = 0; ClassA a1; Scatola s0, s1(a1); void corpo() { cout << "inizia corpo() \n"; ClassA a2; ClassA a3(a2); ClassA a4 = a2; ClassA a5; a5 = a2; cout << "creo un po' di scatole\n" ; Scatola s2(s0); Scatola s3; s3 = s1; cout << "termina corpo() \n"; } int main() { cout << "Inizia il main...\n"; corpo(); cout << "termina il main.\n"; return 0; }
coda
CodaInt
teniamo il puntatore first
al primo e
last
all'ultimo Nodo
, in modo da poter aggiungere un nodo alla coda modificando
il puntatore last->next
. La coda sarà vuota quando last == NULL
; aggiungere un elemento
in questo caso richiederà di creare un Nodo
e assegnare il suo indirizzo tanto a
first
quanto a last
. Provare poi a far eseguire alternativamente un numero random
di push_back e di get_front, mostrando che cosa si accoda e che cosa si preleva dalla coda.
L'ultima operazione stamperà tutti gli elementi ancora in coda.
#include <iostream> #include <iomanip> #include <cstdlib> #include <ctime> using namespace std; class CodaInt { private: // il tipo nodo è dichiarato localmente, visibile solo dentro CodaInt struct Nodo { int i; Nodo *next; Nodo( int i ) : i(i), next(NULL) {}; // ci teniamo il distruttore di default; }; struct Nodo *first, *last; public: CodaInt() : first(NULL), last(NULL) { }; ~CodaInt() { Nodo *pt; for (pt = first; /* OMISSIS */; /* OMISSIS */ ) delete pt; } bool isEmpty() const { return first == NULL; } CodaInt &put_back( int i ) { Nodo *aux = new Nodo(i); if (first!=NULL) { /* OMISSIS */ } else { /* OMISSIS */ } return *this; } int get_front() { if (first == NULL) { return -1; // non è la cosa giusta da fare } else { int i = first->i; // il secondo elemento deve diventare il primo // il primo elemento deve essere distrutto return i; } } }; int main() { int i; CodaInt queue; srand( time( NULL ) ); for (i = 0; i < 20; i++) { int ii = rand() % 1000; cout << setw(5) << ii ; queue.put_back(ii); } cout << endl; while (!queue.isEmpty()) { cout << setw(5) << queue.get_front() ; } cout << endl; }
#include <iostream> #include <cmath> using namespace std; int gcd( int a, int b ) { int c; a = abs(a); b = abs(b); if (b > a) { c = b; b = a; a = c; } for ( ; b > 0; c = a%b, a = b, b = c) ; return a; } class QExt { private: int r, a, b, d; public: QExt(int r0, int a0, int b0, int d0=1) : r(r0), a(a0), b(b0), d(d0) { int c; // d = 0 va rifiutato in malo modo if (d < 0) { a = -a; b = -b; d = -d; } c = gcd(gcd(a,b), d); if (c > 1) { a/=c; b/=c; d/=c; } } friend QExt operator + ( const QExt &x, const QExt &y ) { int d, a, b, r; if (x.r != y.r) { exit(-1); } r = x.r; d = x.d*y.d/gcd(x.d,y.d); a = x.a*d/x.d+y.a*d/y.d; b = x.b*d/x.d+y.b*d/y.d; return QExt(r,a,b,d); } friend ostream & operator << ( ostream & os, const QExt &x ) { // deve essere molto meglio di cosi' os << "[(" << x.a << " + " << x.b << "*R(" << x.r << ")) / " << x.d <<"] "; return os; } }; int main() { QExt a1( 5, 2, -4, -4 ), a2(5, 1, -3); cout << "a1 = " << a1 << endl; cout << "a2 = " << a2 << endl; cout << "a1+a2 = " << a1+a2 << endl; return 0; }Poi si completi la definizione delle operazioni aritmetiche, compreso il passaggio al reciproco, da scrivere nel codice come 1/(irraz quadratico). Finalmente, si provi a verificare che lo sviluppo in frazione continua ha sempre una coda periodica.
x
:
x0 = a0 + r0, dove a0 =Per implementare questa sequenza di operazioni si deve disporre (almeno) di un metodo che fornisce il più grande intero che non supera l'irrazionale quadraticofloor
(x0); se x0 è un numero intero si ha che r0 = 0 ed abbiamo terminato; altrimenti x1 = 1/r0 x1 = a1 + r1, dove a1 =floor
(x1); x2 = 1/r1 x2 = a2 + r2, dove a2 =floor
(x2); . . . . .
QExt x
; si deve poter togliere ad x
la sua parte
intera, e
prendere il reciproco del risultato.an
sono definitivamente periodiche se esistono due numeri k
e
p
per cui
ak+n
= ak+p+n
per ogni; questo avviene se e soltanto se
xk
= xk+p
.iQExt
per contenere l'intero an
e il relativo termine xn
. iQExt a_x[100]
, lo si riempia con i valori generati dalla procedura sopraindicata e si determini
l'antiperiodo e il periodo per un fissato irrazionale quadratico. Servirà poi anche di poter confrontare per uguaglianza due istanze di
iQExt
… vector
; potremmo dichiarare un vettore
vector<iQExt> a_x;
, popolarlo con il metodo a_x.push_back(nuovo_termine)
;
con i primi 100 termini generati calcolando lo sviluppo in frazione continua.
Se 100 termini non sono sufficienti, possiamo (via pus_back
) aggiungerne altri e proseguire
il calcolo.MyStream<T>
i cui elementi vengono costruiti
con due parametri,
un valore iniziale di tipo T
ed
un puntatore a funzione che prende un parametro T
e restituisce un valore T
;
come variabile membro, deve contenere un
vector<T> elems
che all'inizio riceve il valore iniziale.
Per oggetti di questa classe, definiamo il metodo operator []
in modo che
faccia la magia:
se il vettore elems
contiene l'elemento di indice i
, lo restituisce;
altrimenti calcola tutti gli elementi mancanti, fino a elem[i]
e poi restituisce
quest'ultimo.z
è un numero positivo e
x = [a0:a1, … ,an-1,z]
, cioè se
x = a0 + 1/( a1 + 1 / ( … + 1/( an-1 + 1/z ) … ))
allora
pn-2 + z pn-1 x = ---------------- qn-2 + z qn-1Rappresentiamo i coefficienti
p
e q
come interi long
(o magari long long
)
e calcoliamoli secondo Wikipedia.
Dall'identità
pn-2 + x pn-1 x = ---------------- qn-2 + x qn-1potremo così ricavare l'equazione di secondo grado soddisfatta dalla porzione periodica; tale equazione avrà un discriminante Δ con un solo fattore primo
r
elevato a potenza dispari;
avremo bisogno di determinare la radice quadrata del numero intero Δ/r
che potrebbe essere molto grande.
Per questo si consiglia il link per la funzione
isqrt_iterative
#include <math.h> #include <iostream> using namespace std; class Cplx { private: double re, im; public: Cplx() : re(0), im(0) {}; Cplx(double x) : re(x), im(0) {}; Cplx(double x, double y) : re(x), im(y) {}; Cplx(Cplx& q) : re(q.re), im(q.im) {}; double Re() { return re; } double Im() { return im; } double Rho() { return sqrt(re*re+im*im); } double Theta() { return atan2(im,re); } Cplx& operator+= (Cplx& inc) { re += inc.re; im += inc.im; return *this; } friend Cplx operator+ (Cplx& z1, Cplx& z2) { return Cplx( z1.re+z2.re, z1.im+z2.im ); } }; int main() { Cplx z(1,1), w(3,1), z1; cout << z.Theta() << " " << w.Theta() << "\n"; z1 += Cplx(5) += Cplx(0,-17); cout << z1.Re() << " " << z1.Im() << " \n"; z1 = Cplx(-34) + Cplx(0,17); cout << z1.Re() << " " << z1.Im() << " \n"; return 0; }Completare la definizione delle operazioni sui complessi e provarle...
const Cplx z2(1,1);
e le
istruzioni
z1 = z2+z2; cout << z1.Re() << " " << z1.Im() << " \n";Che cosa accade? Come si supera il problema? Poi, far stampare
z2
con
l'istruzione
cout << z2.Re() << " " << z2.Im() << " \n";Che cosa si può rendere costante in una funzione che non ha parametri?
#include <iostream> using namespace std; // questo programma definisce la classe Pila di interi // la profondita' massima della pila e' fissata staticamente class Pila { private: int n; int elem[50]; public: Pila( ) : n(0) {} int getNelem( ) const { return n; }; int top( ) const { if ( n > 0 ) return elem[ n-1 ]; else return -1; // dovrei segnalare una eccezione! }; int pop( ) { if ( n > 0 ) return elem[--n]; else return -1; // dovrei segnalare una eccezione! }; void push( int newEl ) { if ( n < 50 ) elem[n++] = newEl; else return; // dovrei segnalare una eccezione! }; }; int main( ) { int i,j ; Pila Stk1, Stk2; cout << "Stk1 contiene " << Stk1.getNelem() << " numeri" << endl; for (i = 1; i < 8; i++) Stk1.push( i ); for (i = 1; i < 6; i++) Stk2.push( i*i ); cout << "Stk1 contiene " << Stk1.getNelem() << " numeri" << endl; cout << "Stk2 contiene " << Stk2.getNelem() << " numeri" << endl; cout << "Svuotiamo Stk2:" << endl; while (Stk2.getNelem() > 0) cout << Stk2.pop() << ' '; cout << endl; cout << "Svuotiamo Stk1:" << endl; while (Stk1.getNelem() > 0) cout << Stk1.pop() << ' '; cout << endl; return 0; }
throw
, try
,
catch
per gestire le condizioni eccezionali. Un blocco di codice
puo' essere protetto da una try
; se qualche funzione chiamata
direttamente o indirettamente dal codice nel blocco try
lancia con
throw
una eccezione, il controllo passa alla clausola catch
,
che riceve come parametro l'argomento di throw
e gestisce la
condizione.
#include <iostream> using namespace std; // definiamo la classe PilaExcept che useremo per passare informazioni quando push o pop faliscono // // il vettore di stringhe e' static ed e' inizializzato fuori dalla definizione della classe // i possibili valori per le eccezioni sono definiti dalla enum errcode class PilaExcept { public: enum errcode {STACKEMPTY,STACKOVFL}; private: enum errcode tipo; static const char *Reason[2]; public: PilaExcept( enum errcode x ) : tipo(x) {}; const char *ShowReason() const { return Reason[tipo]; } }; const char *PilaExcept::Reason[2] = {"Pila vuota", "Pila piena"}; // ----------------- definizione della classe Pila class Pila { private: int n; int elem[50]; public: Pila( ) : n(0) {}; int getNelem( ) const { return n; }; int top( ) const { if ( n > 0 ) return elem[ n-1 ]; else throw PilaExcept(PilaExcept::STACKEMPTY) ; // segnalo una eccezione! }; int pop( ) { if ( n > 0 ) return elem[--n]; else throw PilaExcept(PilaExcept::STACKEMPTY) ; // segnalo una eccezione! }; void push( int newEl ) { if ( n < 50 ) elem[n++] = newEl; else throw PilaExcept(PilaExcept::STACKOVFL) ; // segnalo una eccezione! }; }; int main( ) { int i; Pila Stk1, Stk2; cout << "Stk1 contiene " << Stk1.getNelem() << " numeri" << endl; for (i = 1; i < 8; i++) Stk1.push( i ); for (i = 1; i < 6; i++) Stk2.push( i*i ); cout << "Stk1 contiene " << Stk1.getNelem() << " numeri" << endl; cout << "Stk2 contiene " << Stk2.getNelem() << " numeri" << endl; cout << "Svuotiamo Stk2:" << endl; while (Stk2.getNelem() > 0) cout << Stk2.pop() << ' '; cout << endl; cout << "Svuotiamo Stk1:" << endl; while (Stk1.getNelem() > 0) cout << Stk1.pop() << ' '; cout << endl; try { for (i = 1; i < 60; i++) Stk1.push(i); } catch (PilaExcept e) { cout << "Eccezione in Stk1: " << e.ShowReason() << endl; } try { for (i = 1; i < 60; i++) cout << Stk1.pop() << ", " ; cout << endl; } catch (PilaExcept e) { cout << "\nEccezione in Stk1: " << e.ShowReason() << endl; } for (i = 1; i < 60; i++) Stk1.push(i); return 0; }Dato però che la condizione di pila piena capita troppo facilmente, è opportuno modificare la definizione della classe Pila in modo che ogni istanza venga creata con spazio per 10 interi e mantenga due numeri, il numero degli elementi presenti e il numero massimo degli elementi che può contenere.
elem
dovrà diventare un int *; nel
costruttore sarà inizializzata con una istruzione del tipo
elem = new int[10];e la variabile
NumMax
sarà inizializzata a 10. Sarà
da definire esplicitamente un distruttore della classe Pila, che dovrà
liberare la memoria allocata per il vettore elem
; per questo la
sintassi è
delete [] elem;La funzione
push
dovrà controllare se c'è spazio per
un nuovo elemento e in tal caso comportarsi come prima; diversamente
dovrà allocare un nuovo vettore di interi di dimensione incrementata di
10 (ad esempio), copiare su questo il contenuto del vettore corrente, liberarlo
e modificare elem
in modo da farlo puntare al vettore aggiornato;
poi, memorizzare il suo argomento.#include <iostream> using namespace std; // Qui il codice del primo esempio di pila e' modificato in modo da avere // senza sforzo aggiuntivo Pile di oggetti di tipo arbitrario, dichiarando // un modello (template) di Pila col tipo rappresentato dal parametro C template<class C> class Pila { private: int n; C elem[50]; public: Pila( ) : n(0) { }; int getNelem( ) const { return n; }; C top( ) const { if ( n > 0 ) return elem[ n-1 ]; else return -1; // dovrei segnalare una eccezione! }; C pop( ) { if ( n > 0 ) return elem[--n]; else return -1; // dovrei segnalare una eccezione! }; void push( C newEl ) { if ( n < 50 ) elem[n++] = newEl; else return; // dovrei segnalare una eccezione! }; }; int main( ) { int i; Pila<int> iStk; Pila<double> dStk; for (i = 0; i < 8; i++) { iStk.push( (int)(i*i) ); } for (i = 0; i < 6; i++) dStk.push( i/5. ); cout << "Svuotiamo dStk:\n"; while (dStk.getNelem() > 0) cout << dStk.pop() << ' '; cout << '\n'; cout << "Svuotiamo iStk:\n"; while (iStk.getNelem() > 0) cout << iStk.pop() << ' '; cout << '\n'; return 0; }Dopo aver scaricato, compilato ed eseguito il programma, modificarlo in modo da avere insieme la classe template, l'allocazione dinamica e le eccezioni.
Dal punto di vista del compilatore, i templates non sono funzioni o classi normali.
Sono compilati a richiesta: il codice di una funzione template non viene
compilato finché non è richiesta una istanza di tale funzione per specifici
argomenti del template.
Se nella dichiarazione della classe template Foo<T>
compare
la dichiarazione
friend ostream& operator<< ( ostream& os, Foo<T> a);questo ci autorizza ad includere nel codice del nostro main, ad esempio, le istruzioni
Foo<int> a(10); std::cout << a << std::endl;
cout
di tipo std::ostream&
e una variabile a
di tipo Foo<int>
.#include <iostream> // compilare ed eseguire; poi scommentare le righe seguenti e ripetere // #define PRINT_INT // #define PRINT_DBL template<typename T> class Foo { public: Foo(T const& value); friend std::ostream& operator<<(std::ostream& o, const Foo<T>& x); private: T value_; }; template<typename T> Foo<T>::Foo(T const& value) : value_(value) { } template<typename T> std::ostream& operator<< (std::ostream& o, const Foo<T>& x) { return o << x.value_; } int main() { Foo<int> a(1); Foo<double> b(3.14); std::cout << "printing a: " << a << "; printing b: " << b << std::endl; return 0; } #if defined(PRINT_INT) std::ostream& operator<< (std::ostream& o, const Foo<int>& x) { return o << x.value_; } #endif #if defined(PRINT_DBL) std::ostream& operator<< (std::ostream& o, const Foo<double>& x) { return o << x.value_; } #endif
Foo<T>
vengano creati automaticamente dal compilatore.Foo<T>
il compilatore deve già sapere che anche operator<<
è
un template che dipende dal tipo T
e dalla classe template Foo<T>
.
Foo
, dicendo solo
che è parametrizzata da T
.
template<typename T> class Foo;
operator<<
template<typename T> std::ostream& operator<< (std::ostream& , const Foo<T>& );
Foo<T>
, si deve
avere un modo per dire che l'operator<<
che è friend
della classe è proprio quello già introdotto come template;
questo si ottiene aggiungendo una lista vuota di parametri di template nella
dichiarazione friend:
friend std::ostream& operator<< <> (std::ostream& o, const Foo& x);
#include <iostream> template<typename T> class Foo; template<typename T> std::ostream& operator<< (std::ostream& , const Foo<T>&); template<typename T> class Foo { public: Foo(T const& value); friend std::ostream& operator<< <> (std::ostream& o, const Foo<T>& x); private: T value_; }; template<typename T> Foo<T>::Foo(T const& value) : value_(value) { } template<typename T> std::ostream& operator<< (std::ostream& o, const Foo<T>& x) { return o << x.value_; } int main() { Foo<int> a(1); Foo<double> b(3.14); std::cout << "printing a: " << a << "; printing b: " << b << std::endl; return 0; }È utile sapere che è possibile avere una specializzazione del template per
operator<<
, per fare in modo che la stampa
di un oggetto di classe Foo<double>
avvenga con una
procedura diversa da quella usata per qualunque altra classe Foo<T>
.
La sintassi per farlo è la seguente:
template<> std::ostream& operator<< (std::ostream& o, const Foo<double>& x) { return o << x.value_ << " (al quadrato: " << x.value_*x.value_ <<")"; }
#include <iostream> using namespace std; // dichiarazione della classe Poly class Poly { private: int deg; double *coef; public: Poly( ); Poly(int n, double *a); Poly( const Poly& q ); Poly& Poly::operator=( const Poly& q ); ~Poly( ); int getDeg( ) const; double *getCoef( ) const; Poly& scale( double k ); Poly& shiftby( const Poly& q ); double eval( double x )const; friend Poly operator+( const Poly& p, const Poly& q ); friend Poly operator*( double k, const Poly& p ); void print( char *nome ) const; }; // implementazione Poly::Poly( ) { coef = new double[1]; deg = 0; coef[0] = 0; } Poly::Poly( int n, double *a ) { deg = n; coef = new double[n+1]; for (int i=0; i<=n; i++) coef[i] = a[i]; } Poly::Poly( const Poly& q ) // copy constructor { deg=q.getDeg(); double *qcoef=q.getCoef(); coef = new double[deg+1]; for (int i=0; i<=deg; i++) coef[i] = qcoef[i]; } Poly& Poly::operator=( const Poly& q ) { if (this != &q) { delete [] coef; deg = q.getDeg(); double *qcoef = q.getCoef(); coef = new double[deg+1]; for (int i=0; i<=deg; i++) coef[i] = qcoef[i]; } return *this; } int Poly::getDeg( ) const { return deg; } double *Poly::getCoef( ) const { return coef; } void Poly::print( char *nome ) const { if (nome != NULL) cout << "Polinomio " << nome; cout << ": grado = " << deg << "; "; for (int i=0; i<=deg; i++) cout << "[" << i <<"]=" << coef[i] << "; "; cout << '\n'; } Poly::~Poly( ) { delete [] coef; } Poly& Poly::scale( double k ) { for (int i = 0; i <= deg; i++) coef[i] *= k; return *this; } Poly& Poly::shiftby( const Poly& q ) { int i; if (q.deg > deg) { double *newvec = new double[q.deg+1]; for (i=0; i<=q.deg; i++) newvec[i] = (i > deg)? 0: coef[i]; delete [] coef; coef = newvec; deg = q.deg; } for (i = 0; i <= q.deg; i++) coef[i] += q.coef[i]; return *this; } double Poly::eval( double x ) const { double val = 0; for (int i = deg; i >= 0; i--) val = val*x+coef[i]; return val; } Poly operator+( const Poly& p, const Poly& q ) { Poly temp(p); temp.shiftby( q ); return temp; } Poly operator*( double k, const Poly& p ) { Poly temp(p); temp.scale( k ); return temp; } main() { int i, n; double *temp, k; // leggiamo due polinomi cout << "assegna grado e coefficienti a_0, a_1, .., a_n di p\n "; cin >> n; temp = new double[n+1]; for (i=0; i<=n; i++) cin >> temp[i]; Poly p(n, temp); delete [] temp; p.print("p"); cout << "p(1) = " << p.eval(1) << "\n"; cout << "assegna grado e coefficienti b_0, b_1, .., b_n di q \n"; cin >> n; temp = new double[n+1]; for (i=0; i<=n; i++) cin >> temp[i]; Poly q(n, temp); delete [] temp; q.print("q"); cout << "q(1) = " << q.eval(1) << "\n"; cout << "\nInizializzo r col valore 7*p+q e lo stampo\n"; Poly r(7*p+q); r.print("r"); cout << "\nDichiaro r1, gli assegno il valore p+q+r e lo stampo\n"; Poly r1; r1 = p+q+r; r1.print("r1"); cout << "\nStampo il polinomio 100*q\n"; (100*q).print("100*q"); cout << "\nMoltiplico q per 4 e lo stampo\n"; q.scale(4.); // scalo q di 4; q.print("q"); // e lo stampo cout << "\nSommo a p il polinomio q e stampo p\n"; p.shiftby(q); // sommo q in p e stampo p; p.print("p"); return 0; }
cout
:
i manipolatori
cout << endl;
la libreria di IO del
C++ consente di controllare vari altri aspetti della formattazione. Segue la
tabella dei manipolatori "senza parametri". Vi sono poi funzioni come setprecision
e setw
che prendono un parametro intero e restituiscono
manipolatori; l'istruzione cout << setw(8);
ha come risultato che il successivo oggetto viene stampato su un campo lungo 8
caratteri (se possibile), mentre cout << setprecision(5);
imposta il numero di cifre dopo la virgola con cui stampare i
numeri floating point.boolalpha | Specifies that variables of type bool appear as true or false in the stream. |
noboolalpha | Specifies that variables of type bool appear as integers 1 or 0 in the stream. |
dec | Specifies that integer variables appear in base 10 notation. |
hex | Specifies that integer variables appear in base 16 notation. |
oct | Specifies that integer variables appear in base 8 notation. |
showbase | Indicates the notational base in which a number is displayed. |
noshowbase | Turns off indicating the notational base in which a number is displayed. |
uppercase | Specifies that hexadecimal digits and the exponent in scientific notation appear in uppercase. |
nouppercase | Specifies that hexadecimal digits and the exponent in scientific notation appear in lowercase. |
fixed | Specifies that a floating-point number is displayed in fixed-decimal notation. |
scientific | Causes floating point numbers to be displayed using scientific notation. |
left | Causes text that is not as wide as the output width to appear in the stream flush with the left margin. |
right | Causes text that is not as wide as the output width to appear in the stream flush with the right margin. |
internal | Causes a number's sign to be left justified and the number to be right justified. |
showpoint | Displays the decimal point even when the fractional part of the number is zero. |
noshowpoint | Displays only the whole-number part of floating-point numbers whose fractional part is zero. |
showpos | Causes positive numbers to be explicitly signed. |
noshowpos | Causes positive numbers to not be explicitly signed. |
unitbuf | Causes output to be processed when the buffer is not empty. |
nounitbuf | Causes output to be buffered and processed when the buffer is full. |
skipws | Cause spaces to not be read by the input stream. |
noskipws | Cause spaces to be read by the input stream. |
// formattazione dell'output attraverso i manipolatori ( includere <iomanip> )
#include <iostream>
#include <iomanip>
using namespace std;
int main( )
{
int i;
double values[] = { 1.23, -4.567, 0.9012, 12, -456.789, 78901, 87699.432, 653.7 };
char *nomi[] = { "Marco", "Matteo", "Luca", "Giovanni",
"Malachia", "Ezechiele", "Belial", "Azazel" };
cout << " Impostare la base (8,16,10) per i numeri interi " << endl;
cout << setbase(8) << 7 <<' '<< 8 <<' '<< 9 <<' '<< 10 << endl;
cout << uppercase;
cout << setbase(16) << 9 <<' '<< 10 <<' '<< 11 <<' '<< 14 <<' '<< 15 <<' '<< 16 << endl;
cout << endl;
cout << oct << 7 <<' '<< 8 <<' '<< 9 <<' '<< 10 << endl;
cout << nouppercase;
cout << hex << 9 <<' '<< 10 <<' '<< 11 <<' '<< 14 <<' '<< 15 <<' '<< 16 << endl;
cout << "\n\n con 'cout << showbase;' si ottiene:" << endl;
cout << showbase;
cout << oct << 7 <<' '<< 8 <<' '<< 9 <<' '<< 10 << endl;
cout << hex << 9 <<' '<< 10 <<' '<< 11 <<' '<< 14 <<' '<< 15 <<' '<< 16 << endl;
cout << dec << noshowbase;
cout << "\n\nfloating senza/con/senza showpoint" << endl;
for (i=0; i<8; i++)
cout << setw(12) << values[i] << (((i+1)%4)? "*": "\n" );
cout << showpoint;
for (i=0; i<8; i++)
cout << setw(12) << values[i] << (((i+1)%4)? "*": "\n" );
cout << noshowpoint;
for (i=0; i<8; i++)
cout << setw(12) << values[i] << (((i+1)%4)? "*": "\n" );
cout << " Lunghezza del campo -- per ogni tipo di dato " << endl;
/* con cout.width(n) oppure cout << setw(n), davanti ad ogni valore da stampare */
for (i=0; i<20; i++)
cout << setw(12) << 90+i << (((i+1)%4)? "*": "\n" );
for (i=0; i<8; i++)
cout << setw(12) << values[i] << (((i+1)%4)? "*": "\n" );
for (i=0; i<8; i++)
cout << setw(12) << nomi[i] << (((i+1)%4)? "*": "\n" );
cout << " --------- ora si modifica con cout << left ----------------- " << endl;
cout << left;
for (i=0; i<20; i++)
cout << setw(12) << 90+i << (((i+1)%4)? "*": "\n" );
for (i=0; i<8; i++)
cout << setw(12) << values[i] << (((i+1)%4)? "*": "\n" );
for (i=0; i<8; i++)
cout << setw(12) << nomi[i] << (((i+1)%4)? "*": "\n" );
cout << " e con cout << right si torna al comportamento iniziale ------ " << endl;
cout << right;
cout << " \n\nNumeri floating: setw imposta la lunghezza del campo, " << endl;
cout << " setprecision il n. di cifre dopo la virgola" << endl;
cout << fixed << setprecision(3);
for (i=0; i<8; i++)
cout << setw(12) << values[i] << (((i+1)%4)? "*": "\n" );
cout << " \n\nNumeri floating formato esponenziale " << endl;
cout << scientific << setprecision(3);
for (i=0; i<8; i++)
cout << setw(12) << values[i] << (((i+1)%4)? "*": "\n" );
cout << "\n\nresetto tutto... o almeno ci provo" << endl;
cout << resetiosflags(ios::scientific);
cout << resetiosflags(ios::fixed);
cout << setprecision(12);
// cout << showpoint;
for (i=0; i<8; i++)
cout << setw(12) << values[i] << (((i+1)%4)? "*": "\n" );
cout << "uso di setfill" << endl;
cout << setfill('*');
for (i=0; i<8; i++)
cout << setw(12) << values[i] << (((i+1)%4)? "*": "\n" );
cout << setfill('_');
for (i=0; i<8; i++)
cout << setw(12) << values[i] << (((i+1)%4)? "*": "\n" );
cout << setfill(' ');
cout << "\n\nstampa dei valori logici con boolalpha e noboolalpha(default) " << endl;
cout << " 3 < 4 is " << (3<4) << endl;
cout << " 5 < 4 is " << (5<4) << endl;
cout<< "\n" << setiosflags(ios::boolalpha);
cout << " 3 < 4 is " << (3<4) << endl;
cout << " 5 < 4 is " << (5<4) << endl;
cout << "\n" << resetiosflags(ios::boolalpha);
cout << " 3 < 4 is " << (3<4) << endl;
cout << " 5 < 4 is " << (5<4) << endl;
return 0;
// The default value for floating-point precision is six.
// For example, the number 3466.9768 prints as 3466.98.
// To change the way this value prints, use the setprecision manipulator.
// The manipulator has two flags: fixed and scientific
// If fixed is set, the number prints as 3466.976800.
// If scientific is set, it prints as 3.4669773+003.
// If either ios::fixed or ios::scientific is set, the precision value
// determines the number of digits after the decimal point.
// If neither flag is set, the precision value determines the total number
// of significant digits. The resetiosflags manipulator clears these flags.
// NB [no]boolalpha non ha alcun effetto per VisualC++ 6.0, mentre
// funziona sulla versione 7.0
}
#include <iostream> using namespace std; #include <string.h> // dichiarazione della classe Supplente ** interfaccia ** class Supplente { protected: char *name; public: Supplente( char *nome ); void fai_lezione(char *s); virtual void inizia(); virtual void concludi(); }; // dichiarazione della classe Annoiato ** interfaccia ** class Annoiato: public Supplente { public: Annoiato( char *nome ); virtual void concludi(); }; // dichiarazione della classe Supplente ** implementazione ** Supplente::Supplente( char *nome ) { name = strdup(nome); } void Supplente:: fai_lezione(char *text) { this->inizia(); cout << "\n " << text; concludi(); cout << "\n"; } void Supplente:: inizia() { cout << "\n<" << name << ":> " << "Buon giorno, ragazzi." ; } void Supplente:: concludi() { cout << "\nPer oggi e' tutto."; } // dichiarazione della classe Annoiato ** implementazione ** Annoiato::Annoiato( char *nome ) :Supplente( nome ) {} void Annoiato:: concludi() { Supplente::concludi(); cout << " [e anche per oggi e' andata]"; } void prof_teaches( Supplente x, char *msg ) { x.fai_lezione( msg ); } void prof_teaches_ptr( Supplente *x, char *msg ) { x->fai_lezione( msg ); } void prof_teaches_ref( Supplente &x, char *msg ) { x.fai_lezione( msg ); } int main( ) { int i; Supplente Marco( "Marco" ); Annoiato Eusebio( "Eusebio" ); Supplente& tizio = Eusebio; // questo e' illegale: Supplente& boh[2] = { Marco, Eusebio }; // facciamoli parlare char *Argomento ="Oggi parleremo della dimostrazione per induzione..."; Marco.fai_lezione(Argomento); Eusebio.fai_lezione(Argomento); prof_teaches( Eusebio, "***" ); prof_teaches_ptr( &Eusebio, "***" ); prof_teaches_ref( Eusebio, "***" ); cout << " 0 per proseguire \n\n"; cin >> i; Supplente docente[2] = { Marco, Eusebio }; for (i = 0; i < 2; i++) { docente[i].fai_lezione(Argomento); } cout << "\n\n"; Supplente *prof[2] = { &Marco, &Eusebio }; for ( i = 0; i < 2; i++) { prof[i]->fai_lezione(Argomento); } return 0; }Derivate dalla classe
Annoiato
una che abbia un comportamento
intermedio; una istanza di tale classe può ad esempio comportarsi per un
numero di volte prefissato nel costruttore come un supplente normale e poi
passare stabilmente al comportamento annoiato. Oppure potete fare in modo che
il suo comportamento venga scelto ogni volta in maniera casuale (magari non in
maniera equiprobabile).
tria.cpp
, graph.h
e graph.cpp
.tria.cpp
#include <stdio.h> #include <iostream> #include <math.h> #include <process.h> #include "graph.h" // prototipi da graph.cpp using namespace std; // ----------------------------- definizione di Pt2 e Tria // class Pt2 { public: double x, y; Pt2( double x = 0, double y = 0) : x(x), y(y) {}; }; class Tria { protected: Pt2 p, q, r; public: Tria( Pt2 p, Pt2 q, Pt2 r ) : p(p), q(q), r(r) { }; void Change( const Pt2&p, const Pt2&q, const Pt2&r ) { this->p = p; this->q = q; this->r = r; } void Disegna() { Scrivi(); MyMove( p.x, p.y ); MyDraw( q.x, q.y ); MyDraw( r.x, r.y ); MyDraw( p.x, p.y ); }; void Cancella() { ::Cancella(); MyMove( p.x, p.y ); MyDraw( q.x, q.y ); MyDraw( r.x, r.y ); MyDraw( p.x, p.y ); }; }; // -------------------- programma principale --------------- int main() { int xp=5,yp=5, xs=500,ys=500; CreaGraph( "Tria Graphs", xp, yp, xs, ys ); SetCoord(0,100,0,100); int c; printf("spostare la finestra lontano dalla finestra grafica\n"); fflush(stdout); getchar(); // Tria t( Pt2(10,10), Pt2(70,10), Pt2(40,43) ); t.Disegna(); cin >> c; t.Cancella(); cin >> c; return 0; }
//graph.h
#include <math.h> #include <windows.h> #include <process.h> HDC CreaGraph( const char* WindowTitle, int x, int y, int xsiz, int ysiz ); // WindowTitle: Titolo della finestra (sulla barra) // x,y : posizione in pixel della finestra (angolo a sinistra in alto) // xsiz, ysize: dimensioni in pixel della finestra void SetCoord( double xmin, double xmax, double ymin, double ymax ); // coordinate virtuali void ClearWin( ); // pulisce la finestra void MyMove( double x, double y ); // posiziona penna void MyDraw( double x, double y ); // trascina penna (= traccia) void Scrivi( ); // imposta a bianco il colore per scrivere void Cancella( ); // imposta il colore per scrivere uguale allo sfondo // il parametro di tipo COLORREF si ottiene con la sintassi // RGB( r, g, b ) // dove r,g,b fra 0 e 255 indicano la quantita' di rosso,verde e blu // RGB(0,0,0) = nero, RGB(255,0,0) = rosso, RGB(r,r,r) = grigi void SetLineCol(COLORREF col); // imposta a col il colore delle linee void MyCirc( double xc, double yc, double r); // disegna una cironferenza void MySetPixel( double x, double y, COLORREF col ); // accende un pixel UINT_PTR SetMyTimer( UINT nmilli, void (*lpAzione)( ) ); // crea un timer che ogni nmilli millisecondi esegue la funz passata come parametro // il valore restituito (0 in caso di fallimento) deve essere conservato per la // funzione KillMyTimer UINT_PTR KillMyTimer( UINT_PTR idTimer );
//graph.cpp
#include <math.h> #include <process.h> #include <windows.h> #pragma comment(lib,"user32.lib") #pragma comment(lib,"gdi32.lib") #pragma comment(lib,"comdlg32.lib") /* nel main: #include "graph.h" nel progetto: aggiungere il file graph.cpp e selezionare use MFC in a shared library // main minimo che usa la finestra grafica: #include <stdio.h> #include "graph.h" int main() { int xp=5,yp=5, xs=500,ys=500; // xp,yp posizione, xs, ys dimensioni (in pixel) CreaGraph( "Finestra Grafica", xp, yp, xs, ys ); SetCoord(0,10,0,10); // coordinate virtuali printf("spostare la finestra lontano dalla finestra grafica\n"); fflush(stdout); getchar(); MyMove(2,2); MyDraw(8,2); MyDraw(8,8); MyDraw(2,8); MyDraw(2,2); getchar(); ClearWin(); MyMove(2,2); MyDraw(8,2); MyDraw(2,8); MyDraw(2,2); getchar(); } */ // -------------- costruzione della finestra grafica ----------------------- #define MYKAPTCOMMAND 0x9753 BOOL SalvaSuFile( HWND, char * ); HPEN myPen, delPen; HPEN curPen; HBRUSH curBrush; HDC hdc, hdcMem; HWND fin; RECT schermo; static double xmin, xmax, ymin, ymax, xf, yf; static int xSiz, ySiz; COLORREF ColSfondo = RGB(20,20,80); // e' il colore dello sfondo HBRUSH eraseBrush = CreateSolidBrush( ColSfondo ); void (*TimerManager) ( ); static UINT_PTR TimerID; UINT_PTR SetMyTimer( UINT uElapse, void (*lpFun)( ) ) { TimerManager = lpFun; return (TimerID = SetTimer( fin, 0, uElapse, NULL )); }; UINT_PTR KillMyTimer( UINT_PTR nIDEvent ) { TimerManager = NULL; return KillTimer( fin, nIDEvent ); }; void ClearWin() { FillRect(hdc, &schermo, eraseBrush); FillRect(hdcMem, &schermo, eraseBrush); } void Scrivi() { SelectObject(hdc, myPen); SelectObject(hdcMem, myPen); } void Cancella() { SelectObject(hdc, delPen); SelectObject(hdcMem, delPen); } void SetLineCol( COLORREF col ) { if (curPen != NULL) DeleteObject( curPen ); curPen = CreatePen( PS_SOLID, 0, col ); SelectObject(hdc, curPen); SelectObject(hdcMem, curPen); } void SetFillCol( COLORREF col ) { if (curBrush != NULL) DeleteObject( curBrush ); curBrush = CreateSolidBrush( col ); SelectObject(hdc, curBrush); SelectObject(hdcMem, curBrush); } void DoKapt() { // char FileName[512] = " "; OPENFILENAME ofn; ofn.lStructSize = sizeof(ofn); ofn.hwndOwner = fin; ofn.hInstance = NULL; ofn.lpstrFilter = "Windows Bitmap\0*.bmp\0Tutti i files\0*.*\0"; ofn.lpstrCustomFilter = NULL; ofn.nMaxCustFilter = 0; ofn.nFilterIndex = 0; ofn.lpstrFile = FileName; ofn.nMaxFile = 512; ofn.lpstrFileTitle = NULL; ofn.nMaxFileTitle = 0; ofn.lpstrInitialDir = NULL; ofn.lpstrTitle = "Salva bitmap"; ofn.Flags = OFN_LONGNAMES; ofn.lpstrDefExt = "bmp"; ofn.lCustData = 0; ofn.lpfnHook = NULL; ofn.lpTemplateName = NULL; if ( !GetSaveFileName( &ofn ) ) return; // SalvaSuFile( fin, ofn.lpstrFile ); } LRESULT CALLBACK MyWindowProc( HWND hWnd, // handle to window UINT Msg, // message identifier WPARAM wParam, // first message parameter LPARAM lParam // second message parameter ) { switch( Msg ) { case WM_TIMER: if (TimerManager != NULL) TimerManager(); return 0; case WM_PAINT: { PAINTSTRUCT ps; HDC loc = BeginPaint( fin, &ps ); int x0 = ps.rcPaint.left, y0 = ps.rcPaint.top; int xS = ps.rcPaint.right-x0, yS = ps.rcPaint.bottom-y0; BitBlt( loc, x0, y0, xS, yS, hdcMem, x0, y0, SRCCOPY ); EndPaint( fin, &ps ); return 0; } break; case WM_SYSCOMMAND: if (wParam == MYKAPTCOMMAND) { DoKapt(); return 0; } else return DefWindowProc( hWnd, Msg, wParam, lParam ); default: return DefWindowProc( hWnd, Msg, wParam, lParam ); } }; // struct per i dati da passare alla funzione GraphThread struct GraphPar { const char *Name; int x, y, xsiz, ysiz; }; void GraphThread( void *arglist ) { struct GraphPar *loc = (struct GraphPar *)arglist; int dx = 2*GetSystemMetrics(SM_CXFRAME); int dy = 2*GetSystemMetrics(SM_CYFRAME) + GetSystemMetrics(SM_CYCAPTION); schermo.left = schermo.top = 0; schermo.right = loc->xsiz; schermo.bottom = loc->ysiz; char *ClassName = "GraphWindow"; WNDCLASS wc = { CS_NOCLOSE, MyWindowProc, 0, 0, NULL, NULL, LoadCursor( NULL, IDC_ARROW ), eraseBrush, NULL, ClassName }; RegisterClass( &wc ); CREATESTRUCT cs; fin = CreateWindowEx( 0,ClassName, loc->Name, WS_POPUPWINDOW|WS_CAPTION & ~ WS_THICKFRAME, loc->x,loc->y,loc->xsiz+dx,loc->ysiz+dy, NULL, NULL, NULL, &cs ); // modifying system menu HMENU hmenu = GetSystemMenu( fin, FALSE ); // MENUITEMINFO mii; mii.cbSize = sizeof(mii); mii.fMask = MIIM_TYPE; mii.fType = MFT_SEPARATOR; InsertMenuItem( hmenu, GetMenuItemCount(hmenu), TRUE, &mii ); char Capture[] = "Catturare immagine..."; mii.fMask = MIIM_TYPE | MIIM_ID; mii.fType = MFT_STRING; mii.wID = MYKAPTCOMMAND; mii.dwTypeData = Capture; mii.cch = sizeof(Capture) -1; InsertMenuItem( hmenu, GetMenuItemCount(hmenu), TRUE, &mii ); ShowWindow( fin, SW_NORMAL ); hdc = GetDC( fin ); hdcMem = CreateCompatibleDC (hdc); HBITMAP hbitmap = CreateCompatibleBitmap( hdc, loc->xsiz, loc->ysiz ); SelectObject (hdcMem, hbitmap); myPen = CreatePen( PS_SOLID, 0, RGB(255,255,255) ); delPen = CreatePen( PS_SOLID, 0, ColSfondo ); SelectObject( hdc, myPen ); SelectObject( hdcMem, myPen ); RECT r; GetClientRect(fin,&r); FillRect( hdcMem, &r, eraseBrush ); MSG msg; int iret; while ((iret = GetMessage(&msg, fin, 0, 0)) != 0 && msg.message != WM_CLOSE) { if (iret == -1) { break; } DispatchMessage( &msg ); } // hdc = hdcMem = NULL; fin = NULL; free(arglist); exit(0); } HDC CreaGraph(const char *WndName, int x, int y, int xsiz, int ysiz) { // lancia il thread che crea la finestra struct GraphPar *gp = (struct GraphPar *)malloc(sizeof(struct GraphPar)); gp->Name = WndName; gp->x = x; gp->y = y; gp->xsiz = xsiz; gp->ysiz = ysiz; hdc = NULL; fin = NULL; xSiz = xsiz; ySiz = ysiz; xmin=ymin=0; xmax=xsiz; ymax=ysiz; xf=yf=1; _beginthread( GraphThread, 5000, (void *)gp ); return hdc; } void SetCoord( double x0, double x1, double y0, double y1 ) { xmin = x0; xmax = x1; ymin = y0; ymax = y1; xf = xSiz/( x1 - x0 ); yf = ySiz/( y1 - y0 ); } void MyMove( double x, double y ) { int ix = (int)((x-xmin)*xf), iy = ySiz-(int)((y-ymin)*yf); MoveToEx( hdc, ix, iy, NULL); MoveToEx( hdcMem, ix, iy, NULL); } void MyDraw( double x, double y ) { int ix = (int)((x-xmin)*xf), iy = ySiz-(int)((y-ymin)*yf); LineTo( hdc, ix, iy); LineTo( hdcMem, ix, iy); } void MyFillCirc( double x, double y, double r ) { int ix = (int)((x-xmin)*xf), iy = ySiz - (int)((y-ymin)*yf); int ir = (int)(r * xf); Ellipse( hdc, ix-ir, iy-ir, ix+ir, iy+ir ); Ellipse( hdcMem, ix-ir, iy-ir, ix+ir, iy+ir ); } void MyCirc( double x, double y, double r ) { // int ix = (int)((x-xmin)*xf), iy = ySiz - (int)((y-ymin)*yf); int ir = (int)(r * xf); Arc( hdc, ix-ir, iy-ir, ix+ir, iy+ir, ix+ir, iy, ix+ir, iy ); Arc( hdcMem, ix-ir, iy-ir, ix+ir, iy+ir, ix+ir, iy, ix+ir, iy ); } void MySetPixel( double x, double y, COLORREF col ) { int ix = (int)((x-xmin)*xf), iy = ySiz - (int)((y-ymin)*yf); SetPixel(hdc, ix, iy, col); SetPixel(hdcMem, ix, iy, col); } // ----------------------------- BOOL SalvaSuFile( HWND hWnd, char *szFileName ) { BITMAPINFO bi; void *pBits=NULL; HDC hDC = hdcMem; int nWidth = xSiz, nHeight = ySiz; SetCursor(LoadCursor(NULL,IDC_WAIT)); ZeroMemory(&bi,sizeof(bi)); bi.bmiHeader.biSize=sizeof(bi.bmiHeader); bi.bmiHeader.biHeight=nHeight; bi.bmiHeader.biWidth=nWidth; bi.bmiHeader.biPlanes=1; bi.bmiHeader.biBitCount=24; bi.bmiHeader.biCompression=BI_RGB; bi.bmiHeader.biSizeImage=3*nWidth*nHeight; HDC hBmpFileDC=CreateCompatibleDC(hDC); HBITMAP hBmpFileBitmap=CreateDIBSection(hDC,&bi,DIB_RGB_COLORS,&pBits,NULL,0); SelectObject(hBmpFileDC,hBmpFileBitmap); BitBlt(hBmpFileDC,0,0,nWidth,nHeight,hDC,0,0,SRCCOPY); HANDLE hFile=CreateFile(szFileName,GENERIC_WRITE,FILE_SHARE_WRITE, NULL,CREATE_ALWAYS,FILE_ATTRIBUTE_NORMAL,NULL); if( hFile != INVALID_HANDLE_VALUE ) { DWORD dwRet=0; BITMAPFILEHEADER bmfHeader; ZeroMemory(&bmfHeader,sizeof(bmfHeader)); bmfHeader.bfOffBits=sizeof(BITMAPFILEHEADER)+sizeof(BITMAPINFOHEADER); bmfHeader.bfSize=bi.bmiHeader.biSizeImage+bmfHeader.bfOffBits; bmfHeader.bfType='MB'; WriteFile(hFile,&bmfHeader,sizeof(bmfHeader),&dwRet,NULL); WriteFile(hFile,&bi.bmiHeader,sizeof(bi.bmiHeader),&dwRet,NULL); WriteFile(hFile,pBits,bi.bmiHeader.biSizeImage,&dwRet,NULL); CloseHandle(hFile); } DeleteDC(hBmpFileDC); DeleteObject(hBmpFileBitmap); SetCursor(LoadCursor(NULL,IDC_ARROW)); return TRUE; }Derivare dalla classe
Tria
una classe
TriaMob
di oggetti dotati di un metodo muovi( )
che
cancella il triangolo dalla posizione corrente, calcola la nuova posizione, lo
sposta e lo ridisegna; il costruttore di TriaMob
deve prendere,
oltre ai vertici, un vettore spostamento e quattro numeri (o due punti) che
rappresentano il rettangolo su cui il triangolo è libero di spostarsi.
Quando uno dei vertici oltrepassa il bordo il triangolo deve invertire la
relativa componente della velocità e rimbalzare.TriaMob
o in una classe parallela ad essa).Sleep(int
nmilli)
che esegue una pausa di nmilli
millisecondi.graph.cpp
sono implementate anche due
funzioni per la creazione e la cancellazione di un timer, che potrebbero essere
usate così:
void Azioni() { // questa funzione viene passata a SetMyTimer // e verrà invocata ogni nmilli millisecondi // finché il timer non viene cancellato // ... le azioni da far svolgere ... } // nel main, o nel costruttore di un qualche opportuno oggetto // (magari una lista di oggetti da far muovere) // si invoca la funzione che crea il timer // UINT_PTR è un tipo definito dal Visual C++, non preoccupatevene int nmilli = 100; // dieci volte al secondo UINT_PTR result = SetMyTimer( nmilli, Azioni ); // nel main, o nel distruttore dell'oggetto di cui sopra, // si invoca la funzione che cancella il timer KillMyTimer( result );
x'' = - a * x', y'' = - a * y' - gcon le condizioni iniziali
x(0) = 0, y(0) = 0, x'(0) = v * cos(alfa), y'(0) = v * sin(alfa;)Al variare dell'alzo da 0 a 90 gradi, l'ascissa del punto di impatto prima cresce e poi decresce.
gun.cpp
con un esempio di come usare le
funzioni di graph.cpp
per tracciare una
curva o per visualizzare una traiettoria.graph.h
e graph.cpp
.
Aggiungere graph.cpp
al progetto, e nel menu Project/settings
scegliereUsing MFC (ad esempio) in a shared DLL.
#include <stdio.h> #include <math.h> #include "graph.h" // prototipi per la finestra grafica // moto bidimensionale // la forza e' la forza di gravita' orientata verso il basso // e una forza di attrito proporzionale con costante c_attr // alla velocita' double c_attr = 0.05; // 0.1; double g = 9.8; double v0 = 400; #define LEN 30000 double pos1[LEN][2]; // x[0],x[1] -- posizione; x[2],x[3] -- velocita' // le componenti di y sono ordinatamente le "derivate" void dery( double x[4], double y[4] ) { y[0] = x[2]; y[1] = x[3]; y[2] = -c_attr*x[2]; y[3] = -c_attr*x[3] - g; } void eulero( double dt, double xv0[4], double xv1[4] ) { int i; double y[4]; dery( xv0, y ); for (i=0; i < 4; i++) xv1[i] = xv0[i]+dt*y[i]; } // potrebbe servire una funzione spara // con dt = passo // xv0 = posizione e velocita' iniziale // pos = vettore delle posizioni // pos[i][0] e' la x al passo i, // pos[i][1] e' la y al passo i // n = dimensionamento del vettore pos // dopo l'esecuzione di npt = spara(dt, vx0, pos, n) // npt contiene il numero di punti scritti nel vettore, // pos[npt-1][1] == 0 e pos[npt-1][0] e' la x di impatto int spara( double dt, double xv0[4], double pos[][2], int n ) { double xv1[4]; int i; pos[0][0] = xv0[0]; pos[0][1] = xv0[1]; for (i = 1; i < n-1; i++) { // si chiama il passo elementare di integrazione al piu' n-1 volte eulero(dt, xv0, xv1); // se la nuova posizione ha la y <= 0, si determina per interpolazione // la x di impatto, si pone pos[i][0]=x e pos[i][1]=0 e si restituisce (i+1); // altrimenti si rimane nel ciclo finche' i < n-1 // .... va scritta! } // si finisce qua se il vettore e' troppo corto; in questo caso si // termina l'esecuzione chiamando la funzione exit fprintf(stderr,"\nvettore posizione troppo corto\n"); exit(0); return -1; } int main() { int i; CreaGraph( "Cannone", 10, 10,800, 350 ); // finestra di 800 pixel per 350, angolo sx in alto in posizione (10,10) printf("spostare la finestra lontano dalla finestra grafica\n"); fflush(stdout); getchar(); SetCoord(-10,790, -10, 340); // il punto in basso a sinistra ha coordinate (-10.,-10.) // il punto in alto a destra ha coordinate (790.,340.) // disegno gli assi MyMove(0,-10); MyDraw(0,340); MyMove(-10,0); MyDraw(790,0); // disegno un quadrato MyMove(100,100); MyDraw(200,100); MyDraw(200,200); MyDraw(100,200); MyDraw(100,100); getchar(); ClearWin(); // cancello la finestra // ridisegno gli assi MyMove(0,-10); MyDraw(0,340); MyMove(-10,0); MyDraw(790,0); double Vet[50][2]; for (i = 0; i < 50; i++) { Vet[i][0] = i*10; Vet[i][1] = 150*(1+sin(i/5.)); } // ho riempito un vettore e ora lo disegno MyMove(Vet[0][0],Vet[0][1]); for (i = 0; i < 50; i++) MyDraw(Vet[i][0],Vet[i][1]); getchar(); /* determinare l'alzo che rende massima la gittata, poi prendere un bersaglio a una distanza 0.75 volte la massima e trovare l'alzo che rende l'errore minore di 1 metro; poi disegnare la traiettoria */ return 0; }
ifs.cpp
su cui
lavoreremo. Le funzioni grafiche sono fornite dal file graph.cpp
che già avete.#include <stdio.h> #include <math.h> #include <process.h> #include <iostream> using namespace std; #include "graph.h" // prototipi da Graph.cpp struct MyPt { double x, y; MyPt(double x1=0, double y1=0): x(x1), y(y1) {}; }; class Trasf { private: double a11, a12, a21, a22; MyPt c; double k,ang; public: Trasf( MyPt c = MyPt(), double k=1, double ad=0 ) : c(c), k(k), ang(ad * 3.14159 / 180.) { a11 = a22 = k * cos( ang ); a12 = -( a21 = k * sin( ang ) ); } MyPt getBase() const { return c; } MyPt operator() ( const MyPt& p ) const { double x = p.x-c.x, y = p.y-c.y; return MyPt( c.x + a11 * x + a12 * y, c.y + a21 * x + a22 * y ); } }; typedef Trasf *ptTrasf; class Ifs { protected: int nTrasf; MyPt base; static COLORREF Ifs::Cols[]; static int nCols ; public: ptTrasf *f; Ifs() : nTrasf(0), f(NULL) {} Ifs(int nTrasf, ptTrasf f[]) { this->nTrasf = nTrasf; this->f = new ptTrasf[nTrasf+1]; for (int i=0; i < nTrasf+1; i++) this->f[i] = f[i]; base = f[0]->getBase(); } Ifs( const Ifs& ref ) { Ifs( ref.nTrasf, ref.f ); } ~Ifs() { if (f) delete[] f; } static friend Ifs merge(const Ifs& ifs1, const Ifs& ifs2) { int i, j; int numT = ifs1.nTrasf + ifs2.nTrasf; ptTrasf *f = new ptTrasf[numT+1]; for (i=0; i< ifs1.nTrasf; i++) f[i] = ifs1.f[i]; for (i=ifs1.nTrasf, j=0; j< ifs2.nTrasf; i++, j++) f[i] = ifs2.f[j]; f[numT] = NULL; return Ifs(numT, f); } void Draw(int Niter=5000); }; COLORREF Ifs::Cols[] = { RGB(255,255,255), RGB(255,0,0), RGB(0,255,0), RGB(0,0,255), RGB(127,127,0), RGB(127,0,127), RGB(0,127,127), }; int Ifs::nCols = sizeof(Ifs::Cols)/sizeof(Ifs::Cols[0]); // implementazione della classe Ifs void Ifs::Draw(int Niter) { MyPt p = base; int i, t; for (i = 0; i < Niter; i++) { t=rand() % nTrasf; base = (*f[t])(base); MySetPixel(base.x,base.y, RGB(128,128,64)); } } // e qui potremmo voler derivare una classe IfsCol che disegna // f_1(K), f_2(K),...,f_n(K) con c colori diversi presi da Ifs::Cols // ridefinendo solo costruttori e Draw class IfsCol : public Ifs { // }; ptTrasf Sierpmaps[] = { new Trasf(MyPt(-.6, -.8), 0.5, 0), new Trasf(MyPt( .6, -.8), 0.5, 0), new Trasf(MyPt( 0, .7), 0.5, 0), NULL }; int nSierpmaps = sizeof(Sierpmaps)/sizeof(Sierpmaps[0]) - 1; ptTrasf Maps1[] = { new Trasf(MyPt(-.3, -.4), 0.5, 45), new Trasf(MyPt( .3, -.4), 0.5, 45), new Trasf(MyPt( 0, .5), 0.5, 45), NULL }; int nMaps1 = sizeof(Maps1)/sizeof(ptTrasf) - 1; ptTrasf Maps2[] = { new Trasf(MyPt( .7, .6), 0.5, 45), new Trasf(MyPt(-.7, .6), 0.5, -45), // new Trasf(MyPt( 0, .6), 0.3, 0), NULL }; int nMaps2 = sizeof(Maps2)/sizeof(ptTrasf) - 1; ptTrasf Maps3[] = { new Trasf(MyPt( .7, -.6), 0.333333, 0), new Trasf(MyPt(-.7, -.6), 0.333333, 0), // new Trasf(MyPt( 0, .6), 0.3, 0), NULL }; int nMaps3 = sizeof(Maps3)/sizeof(ptTrasf) - 1; int main() { int xp=5,yp=5, xs=500,ys=500; CreaGraph( "Iterated Function Systems", xp, yp, xs, ys ); SetCoord(-2,2,-2,2); cout << "spostare la finestra lontano dalla finestra grafica" << flush; getchar(); Ifs ifs1(nMaps1, Maps1); ifs1.Draw(); getchar(); Ifs ifs2(nMaps2, Maps2); Ifs ifs3(nMaps3, Maps3); Ifs ifs4 = merge(ifs1, ifs3); ClearWin(); ifs3.Draw(); getchar(); ClearWin(); ifs2.Draw(); getchar(); ClearWin(); ifs4.Draw(); getchar(); cout << "exiting...\n"; ClearWin(); getchar(); return 0; }Gli insiemi mostrati da questo programma sono generati da Iterated Functions Systems, definiti da un numero finito di applicazioni del piano in sé; se le applicazioni sono contrazioni e K' è l'insieme ottenuto come unione delle immagini di uno stesso compatto K, l'applicazione K --> K' è una contrazione dallo spazio dei compatti del piano in sé rispetto alla distanza di Minkowski, per cui esiste un unico punto fisso, di cui questi insiemi costituiscono una approssimazione dall'interno.
Draw
usa il punto corrente base
, gli applica a caso una delle trasformazioni, invocandone
il metodo operator()
; si ottiene così
un nuovo punto, che viene visualizzato e salvato in base
per l'iterazione successiva.base
viene scelto come il punto fisso della prima applicazione dell'insieme, restituito dal metodo
getBase
.TrasfBase
, senza variabili membro e con due metodi virtuali
MyPt getBase() const
e MyPt operator () (const MyPt &) const
; modificare la classe Trasf
in modo da derivarla da TrasfBase
.Ifs
in modo che lavori in termini di ptTrasf
definito come
typedef TrasfBase *ptTrasf;
invece che come
typedef Trasf *ptTrasf;
... e verificare che funziona ancora tutto.TrasfBase
la classe TrasfCost
. Una istanza funzionante di TrasfCost
è costruita con
TrasfCost trCost(ptFun, a, b);
dove ptFun
è (l'indirizzo di una) funzione di un parametro reale, che restituisce un valore MyPt
,
e [a,b]
è il dominio della funzione.
Dato un punto MyPt curP
il valore restituito da trCost( curP )
non dipende da curP
,
ma è il valore assunto da ptFun
in un punto scelto in modo casuale (via rand()
) nell'intervallo [a,b]
;
il metodo getBase()
potrebbe restituire un valore in ptFun([a,b])
, ad esempio ptFun((a+b)/2])
.TrasfCost
a qualche sistema di IFS con contrazioni affini e osservare il risultato.
#include <iostream> #include <vector> #include <functional> #include <algorithm> #include <iterator> using namespace std; template <class T> class PushToVect { private: vector<T> &dest; public: PushToVect(vector<T>& dest) : dest(dest) {}; void operator( ) ( T i ) { dest.push_back( i+0.123 ); } }; int main() { vector<int> ivec; vector<double> dvec; ivec.push_back(1); ivec.push_back(2); ivec.push_back(3); ivec.push_back(4); cout << ivec.size() << endl; for (unsigned int i=0; i < ivec.size(); i++) cout << ivec[i] << ' '; cout << endl; ostream_iterator<int> scrivi( cout, " -- " ); copy(ivec.begin(),ivec.end(),scrivi); cout << endl; copy(ivec.begin(),ivec.end(),ostream_iterator<int>( cout, " -*- " )); cout << endl; cout << dvec.size() << endl; for_each( ivec.begin(), ivec.end(), PushToVect<double>( dvec )); cout << dvec.size() << endl; copy(dvec.begin(),dvec.end(),ostream_iterator<double>( cout, " _^_ " )); cout << endl; // "analogamente" a quanto si vede per la classe PushToVect // creare una classe template Quadra tale che le istruzioni seguenti // mandino al quadrato tutti gli elementi del vettore ivec // poi, scommentarla e provarla // --------> for_each( ivec.begin(), ivec.end(), Quadra<int>()); copy(ivec.begin(),ivec.end(),ostream_iterator<int>( cout, " _^_ " )); cout << endl; // sfogliare la documentazione e lasciarsi incuriosire, per esempio: // 0. std::transform -- vedere come può essere usato per // mandare al quadrato gli elementi di ivec; // 1. usare std::sort per mettere ivec e dvec in ordine inverso // 2. vedere che cosa fa std::accumulate … return 0; }
#include <iostream> #include <vector> #include <algorithm> #include <iostream> #include <iterator> using namespace std; struct MyStruct{ #define LEN 8 int array[LEN]; friend bool operator< (const MyStruct &s, const MyStruct &t) { // far eseguire un confronto lessicografico delle due strutture } friend ostream& operator<< (ostream &os, const MyStruct & s) { // do something appropriate, then return os; } }; vector<MyStruct> str_a(6); // an array of 6 structures MyStruct str_b[] = { { 1, 5, 8, 6, 3, 2 , 1, 1 }, { 3, 5, 8, 6, 3, 2 , 1, 0 }, { 0, 5, 8, 7, 3, 2 , 1, 0 }, { 0, 5, 8, 6, 3, 2 , 1, 0 }, { 1, 5, 8, 6, 3, 2 , 1, 2 }, { 1, 5, 8, 6, 3, 2 , 1, 0 }, }; bool compare( const MyStruct& a, const MyStruct& b ) { return a < b ; } bool _gt_( int a, int b ) { // ... return something ... } int main(){ // sperimentiamo con gli interi #define SIZE 10 int BigArray[SIZE]; generate(BigArray,BigArray+SIZE,rand); copy(BigArray,BigArray+SIZE,ostream_iterator<int>(cout," , ")); cout << endl << endl; sort(BigArray,BigArray+SIZE); copy(BigArray,BigArray+SIZE,ostream_iterator<int>(cout," , ")); cout << endl << endl; // definiamo una funzione _gt_ per ordinare in senso decrescente, scommentiamo // e proviamo quanto segue // sort(BigArray,BigArray+SIZE,_gt_); copy(BigArray,BigArray+SIZE,ostream_iterator<int>(cout," , ")); cout << endl << endl; // poi riproviamo: STL ha gia' quanto serve: // generate(BigArray,BigArray+SIZE,rand); // sort(BigArray,BigArray+SIZE,greater<int>()); // ora completiamo la definizione di operator< e operator<< // e poi stampiamo tutti i numeri di telefono di str_b // ordinati via STL sort(&str_b[0], &str_b[6], compare); //call to STL sort for (int i = 0; i < 6; i++) cout << str_b[i] << endl; return 0; }
C
che sembra funzionare: lbe_mod.c.C
e verificare
che funzionano; scopo finale, un file con la grafica che faccia i calcoli
giusti.Q-R
basata su di queste; chi abbisogna di un
richiamo di teoria può trovarlo ad esempio sul seguente
link oppure sulle pagine su Wikipedia relative alla
trasformazione di Householder e alla
decomposizione Q-R
.S1 = Succi(0 , S_init) [S1 = ]Succi(1, S1)Se il primo parametro è 0 viene invocata una routine che effettua le inizializzazioni e il secondo parametro deve essere una struttura
S_init
con i campi nx, ny, omega, rho0,
....(o, se preferite, tanti
parametri separati quanti sono gli input) ; il valore in uscita S1
e' una struttura con i campi u, v, rho, f, feq, params
dove params
è un vettore che contiene tutti i parametri usati nella fase di
aggiornamento.S1
i cui valori saranno aggiornati dalla funzione take_step
.S1.u,S1.v
e per visualizzarne le orbite.