Fare copia e incolla dai seguenti esempi:

Programmi in C
StruttureScope delle variabiliChiamata per valore
Dai vettori ai puntatori
Crivello di Eratostene
Operatori bit-wise
Copiare una stringa
Allocazione di memoria, liste, etc.
Semplici metodi di integrazione numerica
Approssimare f' ... insistendo con f'  
Primi esempi di C++:
Overloading e parametri opzionali Un'altra novità: le variabili reference 
Una prima (utilissima) classeInsistendo con Molt: costruttori explicit
Altre sorprese: ordine di costruzione
Costruzione di oggetti con parti: un esempio
La struttura dati coda 
Estensioni quadratiche 
Estensioni quadratiche ... bis 
Numeri complessi  
Ancora una classe: Pila     Di nuovo Pila: le eccezioni 
Classi parametrizzate (templates) 
Templates, friends, specializzazioni 
Una classe per fare calcoli coi polinomi 
Formattazione dell'output con cout: i manipolatori 
Classi derivate 
Classi derivate 2: triangoli 
Some fractals 
Primo esempio su Standard Template Library 
Generate e sort STL 


Files per Lattice Boltzmann (Succi) 
Esperimenti con matlab 
Mescolando Matlab e Lattice Boltzmann 

Progetto Balistica 



Strutture
#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.
Definire una funzione che, dato un triangolo, restituisce il baricentro (il circocentro, l'incentro). Scaricare i file mingraf.cpp, graph.h e graph.cpp.
Copiarli e compilare mingraf.


Poi, usarlo per visualizzare quanto fatto in precedenza.


/* 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;
}





Scope delle variabili
#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);
}



Chiamata per valore
#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;
}




Dai vettori ai puntatori
#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;
}




Crivello di Eratostene
#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.



Operatori bit-wise
#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;
}




Copiare una stringa
#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').
Copiare la stringa contenuta in buf1 nel vettore buf2 con i caratteri in ordine inverso.
Scrivere una funzione reverse che accetta un parametro 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




Allocazione di memoria.
Questo programma alloca una lista di interi letti da tastiera, l'input è terminato da 0; poi stampa i valori contenuti nella lista e infine libera lo spazio allocato. Copiarlo ed eseguirlo, poi svolgere gli esercizi che seguono.
#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.




Semplici metodi di integrazione numerica.
Il programma seguente usa puntatori a funzione; mostra l'andamento degli errori per il metodo di integrazione dei trapezi e per il metodo del punto medio al variare del raffinamento dell'intervallo.
Compilarlo, eseguirlo; poi, modificare la parte di codice che calcola e stampa i risultati in modo da poter aggiungere a piacimento altri metodi.
Provare la modifica con l'aggiunta del metodo di Eulero (di primo ordine) che sul rettangolo elementate restituisce il prodotto della lunghezza della base per il valore della funzione nel primo estremo. Poi, da Wikipedia, numerical integration, trovare i metodi di Simpson e di Simpson 3/8, implementarli e provarli.
#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;
}




Approssimare f'
Il programma che segue approssima la derivata prima di una funzione con differenze finite in avanti, all'indietro e centrate per  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.
Dallo sviluppo di Taylor centrato in 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.



... insistendo con f'
Avendo calcolato a mano i pesi per 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.
Scrivo il sistema come
      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   ...   1
Usando 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.

Come rappresentare queste matrici? Per poter usare la notazione 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.

Nel programma che segue sono state sostituite con ...OMISSIS... alcune parti, che dovrete implementare.
#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.



Overloading e parametri opzionali 
#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;
}




Variabili reference
#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;
}





Una prima utilissima classe
#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_ASSIGNMENT
all'inizio del file e osservare gli errori di compilazione che il compilatore segnala.



Insistendo con Molt: costruttori explicit

Scaricare, compilare ed eseguire il codice che segue.
Il compilatore può usare il costruttore di una classe quando meno ce lo aspettiamo; la keyword 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. 

Nel costruttore di una classe 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.
Se i valori delle variabili membro hanno delle relazioni fra loro, potrebbe sembrare ragionevole usare in qualcuna delle expr_1 , expr_2 , ..., expr_k il valore di qualche variabile membro già inizializzata.
Copiate il codice seguente; tanto 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.
Eseguite il programma. Che cosa accade di strano?

#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;
}





Costruzione di oggetti con parti: un esempio 
#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;
}




La struttura dati coda 
La struttura coda è un contenitore in cui si possono posteggiare degli elementi, e da cui li si possono recuperare nell'ordine di inserimento, e che consente di poter mescolare operazioni di inserimento e di recupero. Possiamo pensarla e realizzarla come una lista di oggetti, in cui il primo elemento è il primo ad essere recuperato, e i nuovi elementi vengono messi appunto in coda dopo quelli già presenti. A tale scopo, dentro 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;
}




Estensioni quadratiche 

Si incolli questo codice e lo si compili.

#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.
Si ricorda la procedura per calcolare i termini dello sviluppo in frazione continua del numero x:
               x0   =    a0 + r0,      dove   a0   =    floor(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);
                . . . . .
            
Per implementare questa sequenza di operazioni si deve disporre (almeno) di un metodo che fornisce il più grande intero che non supera l'irrazionale quadratico QExt x; si deve poter togliere ad x la sua parte intera, e prendere il reciproco del risultato.
Per un irrazionale quadratico (non nullo) la sequenza di operazioni sopra descritta non può mai produrre un resto rn nullo, e quindi non può terminare.
Le "cifre" 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.
Si definisca un nuovo tipo iQExt per contenere l'intero an e il relativo termine xn.
Si dichiari un array 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
Questo naturalmente funziona solo se la somma della lunghezza dell'antiperiodo e del periodo non supera 100.




Estensioni quadratiche ... seconda parte 

Vorremmo poter aggiustare la lunghezza dell'array; ci viene in aiuto il contenitore STL 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.
Ma comunque dovremo scrivere il codice in modo complicato. Sarebbe più comodo se i termini potessero essere calcolati a domanda.

Per questo definiamo una classe (template) 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.
La si potrebbe provare per stampare i primi termini della successione 2^-n.

Per chiudere il cerchio indichiamo il modo per verificare se la frazione continua periodica che abbiamo ottenuto è corretta, ispirandoci al Teorema 1 nella voce Continued_fraction di Wikipedia.
Se 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-1
    
Rappresentiamo 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-1
    
potremo 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




Numeri complessi  

#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...
Aggiungere una dichiarazione di variabile 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?



Ancora una classe: Pila  


#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;   
}





Le eccezioni 

Modifichiamo il programma precedente: se la funzione main() chiama i metodi push o pop di una istanza di Pila troppe volte, quale deve essere il comportamento del programma? Restituire per pop il valore -1, o fare finta di aver eseguito push non e' ragionevole; abortire il programma potrebbe non essere la cosa giusta da fare. Il C++ ha tre keyword, 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.
La variabile membro 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.




Classi parametrizzate (templates)
#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.



Templates, friends, specializzazioni

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;

Il compilatore compilerà  l'ultima istruzione sapendo che dovrà  invocare una funzione di due argomenti, cout di tipo std::ostream& e una variabile a di tipo Foo<int>.
Niente ha detto al compilatore che questa funzione non è disponibile se non la crea lui stesso, come si può vedere compilando ed eseguendo l'esempio seguente:
#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 

Questo toglierebbe molto interesse all'uso dei templates; deve quindi essere possibile fare in modo che anche gli operatori per stampare oggetti di tipo Foo<T> vengano creati automaticamente dal compilatore.
Mentre digerisce la dichiarazione della classe Foo<T> il compilatore deve già sapere che anche operator<< è un template che dipende dal tipo T e dalla classe template Foo<T>.
C'è una circolarità ineliminabile, il che richiede che preliminarmente si introduca la classe template Foo, dicendo solo che è parametrizzata da T.
    template<typename T> class Foo;

poi che si dia il prototipo (più precisamente, un template di propotipo) per l'operatore operator<<
    template<typename T> std::ostream& operator<< (std::ostream& , const Foo<T>& );

e, infine, nella definizione della classe 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);

Il risultato completo è il seguente:
#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_ <<")"; }

Inserite queste linee nel vostro codice, compilate ed eseguite.

 





Una classe per fare calcoli coi polinomi
#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;
}




Formattazione dell'output con cout: i manipolatori


Alla stessa maniera in cui si può inserire un ritorno a capo in uno stream con la sintassi 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
}





Classi derivate
#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).



Classi derivate 2: triangoli

Seguono i file tria.cpp, graph.h e graph.cpp.
Copiarli e passare al testo dell'esercizio.

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.
Per maggiore divertimento, si può aggiungere anche una rotazione attorno al baricentro (dentro TriaMob o in una classe parallela ad essa).
Per rallentare l'esecuzione può essere utile la funzione Sleep(int nmilli) che esegue una pausa di nmilli millisecondi.

Nella versione corrente di 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 );





Balistica
Si deve modellare il moto di un proiettile sparato con velocità iniziale di modulo assegnato e angolo variabile, in presenza di una forza di attrito proporzionale alla velocità; il moto è descritto dal sistema di equazioni differenziali
    x'' = - a * x', 
    y'' = - a * y' - g
con 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.
Determinare la gittata massima ottenibile con g = 9.8, a = 0.05 e velocità iniziale di modulo 400.
Si integri il sistema del primo ordine per il vettore (x,y,x',y') ad esempio con il metodo di Eulero a passo fisso.
Come condizione di uscita prendiamo che due posizioni consecutive abbiano la y che cambia di segno; la ascissa di impatto si otterrà per interpolazione lineare.
Per trovare il massimo di una funzione unimodale, prima crescente e poi decrescente, possiamo usare l'algoritmo seguente:
sull'intervallo [a_n, b_n] prendiamo c_n < d_n, per esempio equispaziati; se la funzione è più grande in c_n, il punto di massimo appartiene all'intervallo [a_n, d_n] e altrimenti appartiene a [c_n, b_n]. Il procedimento si ripete sul nuovo intervallo così ottenuto, finché l'ampiezza non è minore di una tolleranza fissata.

Poi, per bisezione, fissata la posizione del bersaglio si determinino (se ve ne sono) i valori di alfa che consentono di colpirlo.

Segue un abbozzo del file gun.cpp con un esempio di come usare le funzioni di graph.cpp per tracciare una curva o per visualizzare una traiettoria.
La directory corrente deve contenere graph.h e graph.cpp. Aggiungere graph.cpp al progetto, e nel menu Project/settings scegliere
       Using MFC (ad esempio) in a shared DLL. 

gun.cpp
#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;
}



Frattali IFS (iterated function systems)

Di seguito il file ifs.cpp su cui lavoreremo. Le funzioni grafiche sono fornite dal file graph.cpp che già avete.

Codice:
#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.
Il metodo 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.
Il valore iniziale per base viene scelto come il punto fisso della prima applicazione dell'insieme, restituito dal metodo getBase.
Si chiede di inventare una classe 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.
Modificare la classe 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.
Derivare da 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]).
Fatto tutto questo, mescolare qualche TrasfCost a qualche sistema di IFS con contrazioni affini e osservare il risultato.

 
Primo esempio su Standard Template Library
Compilare, eseguire e meditare sul codice che segue.
#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;
}


Generate e sort STL 
		
#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;
}
		
		
		


Files per Lattice Boltzmann (Succi) 
I files originali di S.Succi: lbe.f, un file incluso nelle varie routines lbe.par e un file di input lbe.inp.
Una traduzione in C che sembra funzionare: lbe_mod.c.
Una versione con interfaccia grafica in OpenGL (aggiustata da altri file in rete), che però non funziona come calcoli (forse tradotta male dal Fortran): lbe_gl.c.
Questa versione abbisogna di: glut.h, glut32.lib, glut32.dll, che dovete scaricare; la documentazione sulle funzioni della libreria glut è qua.
Scopi intermedi: compilare/linkare il due sorgenti C e verificare che funzionano; scopo finale, un file con la grafica che faccia i calcoli giusti.


Esperimenti con Matlab 
Dallo help di Matlab familiarizzarsi con:
Funzioni definite dall'utente in Matlab, scritte in Matlab (M-files).
Come esempio, fare esperimenti con le matrici di Householder e con la fattorizzazione 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.
Funzioni definite dall'utente in C: come si scrivono e come si richiamano da dentro Matlab.
Le informazioni e gli esempi vengono da Help > Help Navigator > Matlab > Using Matlab > External Interface/API.
La lista delle funzioni disponibili si trova in Help > Help Navigator > Matlab > Reference> External Interface/API.



Mescolando Lattice Boltzamann e Mex-files... 
Suddividere logicamente la parte di inizializzazione e la parte di aggiornamento del codice di Succi, e scrivere una interfaccia per Matlab.
Le chiamate dovrebbero essere:
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.
Se il primo parametro è 1 il secondo deve essere una struttura S1 i cui valori saranno aggiornati dalla funzione take_step.
Il tutto deve essere realizzato in un unico file C modificando il codice il minimo indispensabile.
Scaricare poi il file di Matlab pplane6.m e provarlo; il passo successivo sarà usare questa funzione per visualizzare il campo di direzioni contenuto in S1.u,S1.v e per visualizzarne le orbite.