martedì 27 novembre 2012

Uso dell'operatore delete

Diversamente dalla memoria locale sullo stack, la memoria del heap deve essere semplicemente liberata. Un volta conclusa l'operazione con il blocco di memoria allocato con l'operatore new, deve essere liberato con l'operatore delete. Questo è ciò che viene fatto nel programma con il blocco di memoria heap che ha immagazzinato 20.
delete pHeap;
La memoria viene restituita a heap per uso futuro. I dati che sono stati immagazzinati non sono più disponibili. Nella riga seguente viene liberata anche la memoria di pHeap2. Un importante argomento da comprendere è che le due istruzioni liberano la memoria su heap ma non interagiscono sulle variabili locali pHeap e pHeap2. Questo crea un potenziale problema in quanto i puntatori segnano una locazione che può essere assegnata dal computer a qualsiasi altra operazione. Questi puntatori sono chiamati puntatori appesi e sono da evitare. Una via per gestire i puntatori appesi è di assegnare valore 0 a loro, il quel viene fatto nel programma con le seguenti istruzioni.

    pHeap =0;
    pHeap2 =0;
Un altro metodo per gestirli è di assegnare loro un indirizzo valido di memoria.

Evitare perdite di memoria

Un problema che il programmatore deve evitare è di allocare memoria e perdere la possibilità di gestirla in qualche modo per poterla liberare in seguito. Quando la memoria viene persa questo è chiamato memory leak. In un programma di grandi dimensioni questo può comportare un vasto problema sino a provocare il crash del sistema. Come programmatore è necessario evitare le perdite di memoria. Nel programma Heap sono state inserite due funzioni le quali creano perdite di memoria, come esempio, in modo da mostrare come non gestire la memoria dinamica in un programma. Nella prima funzione perdita1(), viene allocato un blocco di memoria per un valore intero e quindi si conclude.

 void perdita1()
{ int* dripeti1 = new int(40); }
Se viene chiamata questa funzione, la memoria viene persa per sempre durante il funzionamento del programma. Il problema è che dripeti1, è il solo collegamento con il blocco appena acquisito di memoria su heap, quando la variabile locale cessa di esistere, la funzione perdita1() si conclude. Così non esiste un modo per accedere alla memoria allocata. Per evitare questa perdita di memoria si possono fare due cose, usare l'istruzione delete per liberare la memoria o restituire una copia di dripeti1, e liberare la memoria in un'altra parte del programma.
La seconda funzione creata è perdita2().

void perdita2()
{ int* dripeti2 = new int(50);
dripeti2 = new int(200);
delete dripeti2;
}
In questo caso la perdita di memoria è più sottile, ma esiste comunque. La prima linea della funzione, alloca un blocco di memoria su heap al quale assegna un valore di 50, e dripeti2 punta ad esso. Tuttavia nella seconda istruzione viene, a dripeti2, assegnato un nuovo blocco di memoria al quale viene attribuito valore 200. Infine si cancella il contenuto del blocco di memoria dripeti2. Il problema è che il blocco al quale era stato assegnato valore 50 non ha più alcun puntatore che fa riferimento ad esso e pertanto non può essere liberato. Questo è un altro esempio di perdita di memoria del programma. In questo caso dripeti2 è un puntatore appeso, ma del quale non bisogna preoccuparsi in quanto cessa di esistere al termine della funzione.

lunedì 26 novembre 2012

Allocazione dinamica della Memoria

Dopo aver dichiarato una variabile in C++ viene allocata la memoria necessaria per esso. Una volta che la funzione della variabile ha terminato il suo lavoro, la memoria viene liberata. Questa memoria è utilizzata per le variabili locali viene chiamata stack. Ma esiste un altro tipo di memoria che persiste indipendentemente dalle funzioni del programma. Tu, come programmatore sei incaricato di allocare e liberare questa memoria, collettivamente chiamata heap (o registro libero).
A questo punto si può pensare, “Perché utilizzare un tipo di memoria diversa? Lo stack funziona in modo ottimale per me, grazie.” Tuttavia usare la memoria dinamica del heap offre grandi benefici in termini di efficienza. Usando heap, si occupa la sola memoria necessaria al momento nel quale viene utilizzata, ad esempio se un livello del gioco ha cento nemici, si può allocare la memoria all'inizio del livello e liberarla subito dopo la conclusione dello stesso. Heap inoltre permette di creare un oggetto in una funzione, cui si possa accedere anche al termine della funzione stessa (senza dover restituire una copia dell'oggetto). Si può creare un oggetto a schermo in una funzione e ritornare al suo accesso. Lo strumento della memoria dinamica è molto importante nello sviluppo dei giochi.

Presentare il programma Heap

Il programma Heap è una dimostrazione della memoria dinamica. Il programma alloca la memoria dinamicamente per una variabile intera, ne assegna un valore e quindi la mostra a schermo. Successivamente viene richiamata una funzione che alloca memoria dinamica per un'altra variabile intera, ne assegna il valore e restituisce il puntatore ad essa. Il programma prende i valori del puntatore restituito e lo usa per visualizzare la variabile a schermo, quindi libera la memoria allocata nel heap. Infine vi sono due esempi di utilizzo scorretto della memoria dinamica.

//Heap
//Dimostra la memoria dinamica
#include<iostream>
using namespace std;
int* interosuHeap(); //restituisce intero su Heap
void perdita1(); //creano perdite di memoria
void perdita2();
int main()
{
    int* pHeap = new int;
    *pHeap = 30;
    cout << “*pHeap: ” << *pHeap << endl;
    int* pHeap2 = interosuHeap();
    cout << “*pHeap2: ” << *pHeap2 << endl;
    cout << “Liberare la memoria che punta a pHeap e pHeap2.” << endl;
         delete pHeap;
        delete pHeap2;
        pHeap =0;
       pHeap2 =0;
      return 0;
}
  
 int* interosuHeap()
  {
        int* pTemp = new int(30);
     return pTemp;
   }

void perdita1()
  {  int* dripeti1 = new int(40); }

void perdita2()
  { int* dripeti2 = new int(50);
     dripeti2 = new int(200);
     delete dripeti2;
  }

Uso dell'operatore new

L'operatore new alloca la memoria su heap e restituisce il suo indirizzo. Puoi usare new seguito dal tipo di valore che si vuole riservare lo spazio. Questo è ciò che viene scritto sulla prima linea di main ().
int* pHeap = new int;
La parte della dichiarazione new int alloca abbastanza memoria su heap necessaria per un solo intero e restituisce l'indirizzo di quel blocco di memoria su heap. L'altra parte della dichiarazione int*pHeap, dichiara un puntatore locale, pHeap, il quale punta alla locazione di memoria appena allocata sul blocco heap. Usando pHeap, si può manipolare il blocco di memoria riservato per l'intero. Quello che viene fatto nell'istruzione successiva è di assegnare il valore 30 al blocco di memoria e quindi mostrare il valore immagazzinato, usando pHeap, così come ogni altro puntatore all'intero. La sola differenza è che pHeap punta alla memoria su heap non allo stack.
Uno dei maggiori vantaggi della memoria su heap è che persiste oltre la funzione nella quale viene allocata, significa che creare un oggetto su heap in una funzione e restituire un puntatore o riferimento ad essa.

int* pHeap2 = interosuHeap();
La funzione richiamata interosuHeap(), alloca un blocco di memoria su heap per un intero ed assegna un valore di 30 ad esso.

int* interosuHeap()
{
int* pTemp = new int(30);
return pTemp;}
Quindi la funzione restituisce un puntatore a questo blocco di memoria. Tornando a main(), l'indirizzo del blocco di memoria viene assegnato a pHeap2. In seguito viene visualizzato il contenuto mediante l'istruzione.

cout << “*pHeap2: ” << *pHeap2 << endl;

sabato 24 novembre 2012

Semplice programma per calcolare i numeri primi II

In questa nuova versione il programma consente di ottimizzare l'analisi dei numeri pseudoprimi velocizzando la routine di controllo mediante il Crivello di Eratostene.

#include <cstdlib>
#include <iostream>
#include <math.h>

using namespace std;
bool crivello(long unsigned int numero);

int main(int argc, char *argv[])
{
   
    long unsigned int y,a;
    char risp = 's';
   
    do {
    long unsigned int n_primi=0 , p_primi=0, v_primi=0;  
    cout << "Digita coefficiente a:"; cin >> a;
    for (long unsigned int n=1; n<=1000000; n++){
    y = (2*n^2 + 2*n*a + a);
   
    //Determina se il modulo possiede le caratteristiche corrette
    if ((y%a == 1) || (y%a == (a-1)) || (n%a == 1) || (n%a ==(a- 1))) { cout << " pseudoprimo " << endl;  p_primi++;  
        if (crivello(y)== true) { cout << "numero primo!" << endl; n_primi++; cout << "Numero calcolato:" << y << endl; }
           else cout << "non primo" << endl; 
    }
        //else if (crivello(y)== true) {cout << "numero primo!" << endl; cout << "Numero calcolato:" << y << endl; v_primi++;}
      }
    cout << "Numeri primi trovati:" << n_primi << " pseudo_primi:" << p_primi << endl;
    // Numeri primi non determinati dal modulo
    //cout << "Numeri primi non pseudoprimi:" << v_primi << endl;
    cout << "Vuoi continuare s/n:";
    cin >> risp;
    }while (risp == 's');
   
    system("PAUSE");
    return EXIT_SUCCESS;
}

bool crivello (long unsigned int numero){
     //crivello di Eratostene
     long double radicale = sqrt(numero); //MCD dispari sino a radice(n)
     for (long unsigned int iA =3; iA <= radicale; iA+=2)
         {
            if (numero%iA ==0) return false;
             }
             return true;
     }

giovedì 22 novembre 2012

Sovraccarico degli operatori

Il sovraccarico degli operatori può suonare come qualcosa di strano, da evitare a tutti i costi, ma non lo é. Il termine Operator Overloading permette di dare un nuovo significato agli operatori pre-costruiti usando le nuove tipologie di definizione. Ad esempio può essere sovraccaricato l'operatore * così che quando venga utilizzato nelle matrici 3D (oggetti istanziati da classi da voi definite), il risultato sia la moltiplicazione delle matrici tra loro. Per sovraccaricare un operatore è necessario definire una funzione chiamata operatorX, ove X è l'operatore da sovraccaricare. Questo è quanto viene realizzato nel programma Roditore Amico, con l'operatore <<.
ostream& operator <<(ostream& os, const Roditore& aRoditore)
{
os << “Oggetto Roditore - ”;
os << “m_Nome: ” << aRoditore.m_Nome;
return os;
}

La funzione sovraccarica l'operatore << così quando viene utilizzato l'oggetto Roditore con << in cout, viene mostrato il membro dati m_Nome. Sostanzialmente la funzione permette una facile visualizzazione dell'oggetto Roditore. La funzione accede direttamente al membro privato m_Nome, in quanto viene dichiarata amica della classe Roditore con la seguente linea di codice:
friend ostream& operator <<(ostream& os, const Roditore& aRoditore);

Questo significa che in main() è possibile utilizzare la sola sintassi cout << ctopo; tutto funziona in quanto cout è un tipo di ostream, il quale già sovraccarica << come pre-costruito.

domenica 18 novembre 2012

Semplice programma per calcolare numeri primi

Questo semplice programma consente di calcolare con rapidità numeri primi di piccole dimensioni, i quali possono essere utili nella realizzazione di generatori pseudorandom per il codice del gioco.
Data l'equazione y = 2x2 + 2ax + a
Dal Teorema di Fermat si può ottenere, per ogni a intero positivo dispari, e x intero positivo.
Se y = ±1 mod a allora y è uno pseudoprimo;
Se x= ±1 mod a allora y è uno pseudoprimo.
Tuttavia per confermare che il numero calcolato sia effettivamente primo è indispensabile utilizzare un crivello, nel nostro caso si tratta del crivello di Eratostene, limitato per MCD sino alla radice quadrata di y.
#include <cstdlib>
#include <iostream>
#include <math.h>

using namespace std;
bool crivello(long int numero);

int main(int argc, char *argv[])
{
   
    long int y,a;
    char risp = 's';
   
    do {
    int n_primi =0 , p_primi =0, v_primi=0;  
    cout << "Digita coefficiente a:"; cin >> a;
    for (int n=1; n<=10000; n++){
    y = (2*n*n + 2*n*a + a);
   
    //Determina se il modulo possiede le caratteristiche corrette
    if ((y%a == 1) || (y%a == (a-1)) || (n%a == 1) || (n%a ==(a- 1))) { cout << " pseudoprimo " << endl;  p_primi++;  
        if (crivello(y)== true) { cout << "numero primo!" << endl; n_primi++; cout << "Numero calcolato:" << y << endl; }
           else cout << "non primo" << endl; 
    }
        /*else if (crivello(y)== true) {cout << "numero primo!" << endl; v_primi++;} */
      }
    cout << "Numeri primi trovati:" << n_primi << " pseudo_primi:" << p_primi << endl;
    /*cout << "Numeri primi non pseudoprimi:" << v_primi << endl;*/
    cout << "Vuoi continuare s/n:";
    cin >> risp;
    }while (risp == 's');
   
    system("PAUSE");
    return EXIT_SUCCESS;
}

bool crivello (long int numero){
     //crivello di Eratostene
     long double radicale = sqrt(numero); //MCD sino a radice(n)
     for (long int iA =2; iA <= radicale; iA++)
         {
            if (numero%iA ==0) return false;
             }
             return true;
     }

sabato 17 novembre 2012

Creare Funzioni Amiche

Una funzione amica può accedere ad ogni membro della classe di cui è amica. Puoi specificare che la funzione è amica di una classe dalla lista dei prototipi di funzioni preceduti dalla parola chiave friend dentro la definizione della classe. Questo è ciò che viene fatto all'interno della classe Roditore mediante la seguente linea di codice, la quale dichiara che la funzione globale Spia() è un amica di roditore.
friend void Spia(const Roditore& aRoditore);

Questo significa che Spia() può accedere ad ogni membro della classe Roditore anche se non è funzione membro della classe. Spia() si avvantaggia di questa relazione per accedere ai dati privati del membro m_Nome e mostra il nome del roditore passato tramite funzione.

void Spia(const Roditore& aRoditore)
{ cout << aRoditore.m_Nome << endl; }

martedì 13 novembre 2012

Usare funzioni Amiche e Sovraccarico degli operatori

Le funzioni amiche e il sovraccarico degli operatori sono due concetti avanzati relativi alle classi. Le Funzioni Amiche hanno accesso completo ad ogni membro della classe. Il Sovraccarico degli Operatori permette di definire nuovi significati per gli operatori pre-costruiti e come si relazionano agli oggetti della propria classe. Come vedremo i due concetti possono essere usati assieme.

Presentare il programma Roditore Amico

Il programma Roditore Amico crea un oggetto Roditore. Quindi usa una funzione amica, la quale è in grado di accedere direttamente ai dati privati che sono immagazzinati, come il nome del roditore e quindi mostrarlo a schermo. Infine il programma mostra l'oggetto Roditore inviandolo come output standard. Il codice del programma è il seguente:

//Roditore amico
//Dimostra l'uso delle funzioni amiche e sovraccarico degli operatori
#include <iostream>
#include <string>
using namespace std;
class Roditore
    { //le funzioni amiche sono rese come globali per la classe Roditore
      friend void Spia(const Roditore& aRoditore);
      friend ostream& operator <<(ostream& os, const Roditore& aRoditore);
      public:
                Roditore (const string& nome= “ “): m_Nome(nome){}
      private:
                 string m_Nome;
    };
  void Spia(const Roditore& aRoditore);
  ostream& operator <<(ostream& os, const Roditore& aRoditore);
int main()
{
   Roditore ctopo(“Topolino”);
   cout << “Chiama Spia() per accedere a ctopo dati privati, m_Nome:\n”;
   Spia(ctopo);
    cout << “\n Invia oggetto ctopo a cout con operatore << :\n”;
    cout << ctopo;
   return 0;
  }
// funzione amica globale che può accedere a tutti i membri dell'oggetto Roditore
void Spia(const Roditore& aRoditore)
  { cout << aRoditore.m_Nome << endl;
     }
// funzione amica globale che può accedere a tutti i membri dell'oggetto Roditore e
// sovraccarica operatore << in modo da inviare oggetto Roditore a cout
ostream& operator <<(ostream& os, const Roditore& aRoditore)
 {
    os << “Oggetto Roditore - ”;
    os << “m_Nome: ” << aRoditore.m_Nome;
    return os;
  }

sabato 10 novembre 2012

Uso dei Contenitori di dati

E' possibile utilizzare i contenitori come dati membro per gli oggetti. Questo viene chiamata Fattoria. Per il singolo membro dati viene dichiarata la classe come un semplice vettore che trattiene gli oggetti Roditore chiamato m_Roditori.
vector<Roditore> m_Roditori;

Quando viene istanziato un nuovo oggetto fattoria con l'istruzione:
Fattoria miaFattoria(3)

Viene richiamato il costruttore:
Fattoria(int spazi =1) { m_Roditori.reserve (spazi); }

il quale alloca memoria per i tre oggetti Roditore nel vettore m_Roditori dell'oggetto Fattoria. Subito dopo io aggiungo tre roditori alla fattoria richiamando la funzione Somma dell'oggetto Fattoria.
miaFattoria.Somma( Roditore(“Minni”));
miaFattoria.Somma( Roditore(“Mortimer”));
miaFattoria.Somma( Roditore(“Topps”));

La seguente funzione membro accetta una costante di riferimento a un oggetto Roditore il quale aggiunge una copia dell'oggetto al vettore m_Roditori.
void Somma( const Roditore& aRoditore) { m_Roditori.push_back(aRoditore); }

Infine viene chiamato l'appello di tutti roditori tramite la funzione Richiamo().
miaFattoria.Richiamo();

La quale controllo il vettore e richiama ogni elemento Roditore tramite la funzione DaiNome(), così ogni topo fornisce il suo nome.

venerdì 9 novembre 2012

Usare i Dati dell''oggetto

Un modo per usare l'aggregazione quando viene definita una classe è dichiarare un membro dati che possa contenere un altro oggetto. Questo è il motivo per cui Roditore viene dichiarato con la seguente linea, il quale dichiara il dato m_Nome come oggetto stringa.
string m_Nome;

Solitamente, si usa aggregazione quando un oggetto possiede un altro oggetto. In questo caso, il roditore ha un nome. Questo tipo di relazioni sono chiamate di “possesso”. Viene posto nella dichiarazione del roditore il suo nome, in questo modo quando viene istanziato un nuovo oggetto con:
Roditore ctopo(“Topolino”);

Il quale richiama il costruttore del Roditore:
Roditore(const string& nome = “”): m_Nome(nome){}

Dal passaggio della stringa letterale “Topolino”, il costruttore viene chiamato e un oggetto stringa con il nome viene istanziato, il quale è assegnato a m_Nome. Un nuovo roditore di nome Topolino nasce. Infine viene mostrato il nome del roditore attraverso la seguente linea di codice:
cout << “Il mio nome e' ” << ctopo.DaiNome() << endl;

Il codice ctopo.DaiNome() restituisce una copia dell'oggetto stringa con il nome del roditore, il quale viene inviato a cout e mostrato su schermo.

mercoledì 7 novembre 2012

Usare Aggregazione

Gli oggetti di un gioco sono spesso combinazioni di altri oggetti. Per esempio in un gioco di gare, un auto da corsa può essere vista come la combinazione di singoli oggetti quali il corpo, le ruote, e un motore. Altre volte si può vedere un singolo oggetto come una collezione di oggetti correlati tra loro. La simulazione di un guardiano di zoo, si può vedere lo zoo come una collezione di un arbitrario numero di animali. Questo tipo di relazioni possono essere imitate tra gli oggetti mediante uso dell'aggregazione in OOP. Per esempio si può scrivere una classe Auto_Corsa, la quale ha un dato membro motore, il quale è un oggetto Motore. Oppure si può scrivere la classe Zoo che ha gli animali come membro dati, i quali sono una collezione di oggetti Animali.

Programma Fattoria di roditori

Il programma fattoria di roditori definisce un nuovo tipo di roditore dotato di nome. Dopo che il programma annuncia il nome del roditore, crea una fattoria di roditori – Una collezione di roditori. Infine il programma richiama tutta la fattoria ed ogni roditore si presenta con il nome. Seguito il codice del programma:
// Fattoria di roditori
// Dimostra contenitori di oggetti
#include <iostream>
#include <string>
#include <vector>
using namespace std;
class Roditore
{
    public:
                  Roditore(const string& nome = “”): m_Nome(nome){}
                  string DaiNome( ) const { return m_Nome; }
     private:
                 string m_Nome;
};
class Fattoria
{
   public:
               Fattoria(int spazi =1) { m_Roditori.reserve (spazi); }
               void Somma( const Roditore& aRoditore) { m_Roditori.push_back(aRoditore); }
               void Richiamo() const
                 { for (vector<Roditore>::const_iterator iter = m_Roditori.begin(); iter != m_Roditori.end(); ++iter)
          cout << iter -> DaiNome() << “ qui.\n”;
              }
   private:
               vector<Roditore> m_Roditori;
};

int main()
{
      Roditore ctopo(“Topolino”);
      cout << “Il mio nome e' ” << ctopo.DaiNome() << endl;
      cout << “Creiamo la fattoria” << endl;
      Fattoria miaFattoria(3)
      cout << “Aggiungi 3 topi alla fattoria.\n”;
      miaFattoria.Somma( Roditore(“Minni”));
      miaFattoria.Somma( Roditore(“Mortimer”));
      miaFattoria.Somma( Roditore(“Topps”));
      cout << “Appello\n”;
      miaFattoria.Richiamo();

   return 0;
}

lunedì 5 novembre 2012

Classi avanzate e memoria dinamica

Il linguaggio C++ fornisce al programmatore di giochi un valore alto di controllo sul computer. Una delle abilità fondamentali è il controllo diretto della memoria. In questi post, verranno spiegati i concetti di memoria dinamica, una memoria gestita da voi stessi. Ma è necessario fare attenzione per evitare quelle trappole che la memoria dinamica crea e saperle evitare. Specificatamente si imparerà a:
  • Combinare oggetti
  • Usare funzioni amiche
  • Sovraccaricare gli operatori
  • Allocare dinamicamente e liberare la memoria
  • Evitare perdite di memoria
  • Produrre copie profonde degli oggetti

Usare Aggregazione

Gli oggetti di un gioco sono spesso combinazioni di altri oggetti. Per esempio in un gioco di gare, un auto da corsa può essere vista come la combinazione di singoli oggetti quali il corpo, le ruote, e un motore. Altre volte si può vedere un singolo oggetto come una collezione di oggetti correlati tra loro. La simulazione di un guardiano di zoo, si può vedere lo zoo come una collezione di un arbitrario numero di animali. Questo tipo di relazioni possono essere imitate tra gli oggetti mediante uso dell'aggregazione in OOP. Per esempio si può scrivere una classe Auto_Corsa, la quale ha un dato membro motore, il quale è un oggetto Motore. Oppure si può scrivere la classe Zoo che ha gli animali come membro dati, i quali sono una collezione di oggetti Animali.

sabato 3 novembre 2012

Dichiarare e inizializzare una Variabile dati Statica

Una variabile dati statica è un singolo membro dati che esiste per l'intera classe. Nella definizione della classe, io definisco un membro dati statico per immagazzinare il numero degli oggetti Roditore che sono stati istanziati, usando la seguente linea di codice, la quale dichiara il dato i_Totali.
static int i_Totali; //membro dati statico, numero dei roditori
Possono essere dichiarati i propri dati statici come mostrato nella riga di esempio così come ho fatto, la dichiarazione deve essere preceduta dalla parola chiave static. Inoltre è possibile dare il prefisso al nome della variabile s_nome, in modo da riconoscerla immediatamente. Al di fuori della definizione della classe, inizializzo il valore della variabile statica assegnandogli 0 ad essa.
int Roditore::i_Totali =0; //inizializzazione membri dati statici

Da notare che i membri dati sono qualificati tramite Roditore:: come necessario per le definizioni esterne alla classe, il membro dati deve essere associato al nome di essa. In questo modo il programma sa che il valore assegnato alla classe Roditore è 0.

Accesso alle variabili dati statiche

E' possibile accedere alle variabili dati statiche che siano pubbliche da qualsiasi parte nel programma. In main(), si accede a i_Totali con la seguente linea, la quale mostra valore 0 ed il numero dei Roditori istanziati.
cout << “Il numero totale dei roditori e': ”;
cout << Roditore::i_Totali <<endl;

E' inoltre possibile accedere ai dati statici all'interno del costruttore di Roditore, mediante la seguente linea che incrementa i_Totali.
++i_Totali;

Questo significa che ogni volta che un oggetto viene istanziato, il valore di i_Totali viene incrementato. Da notare che all'interno del costruttore non vi è la necessita di qualificare il nome della variabile tramite Roditore::
Tuttavia in questo caso i membri statici sono stati dichiarati come pubblici, è possibile renderli anche privati, ma come ogni altro membro dati, vi può accedere solo una funzione membro della classe.

Definire una Funzione membro statica

Una funzione membro statica esiste per l'intera classe. Viene definita nella classe Roditore, una funzione membro che ci permette di ottenere il valore totale degli oggetti istanziati (i_Totale), con le seguenti linee di codice:
static int DaiTotali() //funzione membro statica
{ return i_Totali; } 

E' possibile definire la funzione statica come tutte le altre funzioni, solo deve essere preceduta nella definizione dalla parola chiave static.

venerdì 2 novembre 2012

Uso dei Dati e Funzioni Membro statiche

Gli oggetti sono ottimi in quanto ogni istanza permette di memorizzare un proprio set di dati, fornendogli un'unica identità. Ma se volessimo memorizzare delle informazioni riguardanti l'intera classe, ad esempio il numero delle istanze esistenti? Si può desiderare di creare un insieme di nemici che combattano contro il giocatore, basando il loro comportamento sul numero totale. Per esempio che raggiunta una certa soglia non ne vengano creati altri dal gioco, e sconfitti dal giocatore al di sotto di una certa soglia, fuggano. E' possibile immagazzinare questo numero all'interno di ogni oggetto, tuttavia sarebbe uno spreco di memoria, più il lavoro necessario ad aggiornare tutti gli oggetti contemporaneamente. Invece ciò può essere fatto con i membri dati statici, il modo migliore utilizzato per accedervi è attraverso funzioni membro statiche.

Presentare il programma Roditore Statico

Il Roditore Statico dichiara un nuovo tipo di roditore dotato di una variabile dati statica che immagazzina il numero totale di roditori che sono stati creati. Inoltre definisce delle funzioni statiche necessarie per mostrare questo valore. Per prima cosa il programma fa istanza dei nuovi oggetti roditore, e ne mostra il numero totale accedendo direttamente al membro dati statico. Successivamente il programma fa istanza 3 nuovi roditori. Quindi mostra il numero totale attraverso l'uso di una funzione membro statica. Questo è il codice:
//Roditore Statico
//Dimostra l'uso di funzioni e membri dati statici
#include <iostream>
using namespace std;
class Roditore
{
            public:
                      static int i_Totali; //membro dati statico, numero dei roditori
                      Roditore(int fame =0): i_Fame(fame)
                         { cout << “E' nato un nuovo roditore”; << endl; ++i_Totali; }
                      static int DaiTotali() //funzione membro statica
                       { return i_Totali; }
private:
int i_Fame;
};
    int Roditore::i_Totali =0; //inizializzazione membri dati statici
int main()
{
      cout << “Il numero totale dei roditori e': ”;
      cout << Roditore::i_Totali <<endl;
      Roditore topo1, topo2, topo3;
      cout << “Il numero totale dei roditori e': ”;
      cout << Roditore::DaiTotali()<< endl;
    return 0;
}