Visualizzazione post con etichetta assegnazione. Mostra tutti i post
Visualizzazione post con etichetta assegnazione. Mostra tutti i post

sabato 26 gennaio 2013

Animare con OpenGL

Il programma elencato di seguito crea una semplice animazione realizzata utilizzando le funzioni OpenGL.

#include <GL/gl.h>
#include <GL/glu.h>
#include <GL/glut.h>
#include <stdlib.h>

static GLfloat spinta = 1.0;

void inizia(void)
{
   glClearColor (0.0, 0.0, 0.0, 0.0);
   glShadeModel (GL_FLAT);
}

void mostra(void)
{
   glClear(GL_COLOR_BUFFER_BIT);
   glPushMatrix();
   glRotatef(spinta, 0.0, 0.0, 1.0);
   glColor3f(0.0, 0.0, 1.0);
   glRectf(-25.0, -25.0, 25.0, 25.0);
   glPopMatrix();
   glutSwapBuffers();
}

void spinMostra(void)
{
   spinta = spinta + 2.0;
   if (spinta > 360.0)
      spinta = spinta - 360.0;
   glutPostRedisplay();
}

void ridisegna(int w, int h)
{
   glViewport (0, 0, (GLsizei) w, (GLsizei) h);
   glMatrixMode(GL_PROJECTION);
   glLoadIdentity();
   glOrtho(-50.0, 50.0, -50.0, 50.0, -1.0, 1.0);
   glMatrixMode(GL_MODELVIEW);
   glLoadIdentity();
}

void topo(int bottone, int stato, int x, int y)
{
   switch (bottone) {
      case GLUT_LEFT_BUTTON:
         if (stato == GLUT_DOWN)
            glutIdleFunc(spinMostra);
         break;
      case GLUT_MIDDLE_BUTTON:
         if (stato == GLUT_DOWN)
            glutIdleFunc(NULL);
         break;
      default:
         break;
   }
}

/*
 *  funzioni di doppio buffer immagine
 *  funzioni che registrano lo stato del mouse
 */
int main(int argc, char** argv)
{
   glutInit(&argc, argv);
   glutInitDisplayMode (GLUT_DOUBLE | GLUT_RGB);
   glutInitWindowSize (300, 300);
   glutInitWindowPosition (125, 125);
   glutCreateWindow (argv[0]);
   inizia ();
   glutDisplayFunc(mostra);
   glutReshapeFunc(ridisegna);
   glutMouseFunc(topo);
   glutMainLoop();
   return 0;
}

venerdì 28 dicembre 2012

Presentare il Polimorfismo

Uno dei pilastri della OOP è il polimorfismo, con il quale le funzioni membro producono risultati differenti a seconda del tipo di oggetto nel quale sono stati chiamate in causa. Per esempio, un gruppo di nemici che il giocatore sta affrontando, il gruppo è formato da oggetti di tipo diverso imparentati tra loro dall'ereditarietà, i nemici ed i loro capi. Attraverso la magia del polimorfismo, si possono richiamare le stesse funzioni membro per ogni cattivo del gruppo, definire come attaccare il giocatore, e per ogni oggetto valutarne gli effetti. Una chiamata all'oggetto nemico produce un attacco debole, mentre una chiamata al capo produce risultati diversi, ad esempio un potente attacco. Questo può assomigliare al superamento, ma il polimorfismo è diverso, in quanto le chiamate delle funzioni sono dinamiche e gli effetti valutati in tempo reale, a seconda del tipo di oggetto. Il modo migliore per comprendere questo meccanismo è mostrarne esempi concreti.

Presentare il programma Capo virtuale

Il programma Capo virtuale dimostra come si usano le funzioni membro virtuali e come raggiungere il comportamento polimorfico. Mostra cosa accade quando si usa un puntatore alla classe di base per chiamare funzioni virtuali e non virtuali. Mostra anche l'uso dei distruttori verticali che assicurano la corretta distruzione degli oggetti richiamati tramite puntatori alla classe di base.
// Capo virtuale
//dimostra funzioni virtuali
#include<iostream>
using namespace std;
class Nemico
{public:
            Nemico(int danno=10){ m_pDanno = new int(danno); }
            virtual ~Nemico(){ cout << “m_pDanno cancellato”; delete m_pDanno;}
            void Fastidio() const
              { cout << “Il nemico vuole combatterti.\n”; }
           void virtual vFastidio() const
              { cout << “Il nemico vuole combatterti.\n”; }
protected: int* m_pDanno;
};
class Capo: public Nemico
{
public: Capo( int multi =2){ m_pMultiDanno = new int(multi); }
           virtual ~Capo(){ cout << “m_pMultiDanno cancellato”; delete m_pMultiDanno;}
            void Fastidio() const
             { cout << “Il capo vuole mettere fine alla tua esistenza.\n”; }
           void virtual vFastidio() const
            { cout << “Il capo vuole mettere fine alla tua esistenza.\n”; }
protected: int* m_pMultiDanno;
};
int main()
{ cout << “Puntatore a Nemico che punta a oggetto Capo:\n”;
   Nemico* pCattivo = new Capo();
   pCattivo - > Fastidio();
   pCattivo - > vFastidio();
 cout << “Cancelliamo puntatore a Nemico:\n”;
  delete pCattivo;
  pCattivo = 0;

return 0;
}

Uso dell'operatore di Assegnazione sovraccaricato e costruttore Copia nelle classi derivate

E' già stato spiegato come scrivere un operatore di assegnazione sovraccaricato e un costruttore copia per una classe. Tuttavia, scriverli per una classe derivata richiede un leggero lavoro ulteriore in quanto non sono ereditati dalla classe di base. Quando si sovraccarica l'operatore di assegnazione nelle classi derivate, di solito si richiama l'operatore di assegnazione delle classi base, utilizzando una chiamata esplicita con il nome della classe di base quale prefisso. Se Capo è derivato da Nemico, la funzione dell'operatore di assegnazione definita in Capo di solito recita:
Capo& operator=(const Capo& b)
{ Nemico::operator=(b); //gestisce i dati membro ereditati da Nemico
// di seguito bisogna gestire i dati membro di Capo }

La chiamata esplicita dell'operatore di assegnazione di Nemico gestisce i dati membro ereditati da esso. Ma il resto della funzione deve occuparsi dei dati definiti in Capo. Per il costruttore Copia, di solito si effettua una chiamata al costruttore Copia della classe di base. Se Capo è derivato da Nemico, il costruttore di copia definito in esso, si scrive:
Capo (const Capo& b): Nemico(b){
// si gestiscono i dati membro definiti in Capo }

Dalla chiamata del costruttore Copia di Nemico con Nemico(b), la copia dei dati membro di Nemico viene messa nel nuovo oggetto Capo. Nel resto della funzione di Capo del costruttore copia si bada a tutti i dati membro dichiarati solo in Capo del nuovo oggetto.

mercoledì 5 dicembre 2012

Sovraccarico operatore Assegnazione

Quando entrambe le istruzioni di assegnazione sono oggetti della stessa classe, la funzione dell'operatore di assegnazione della classe viene chiamata. Come il costruttore copia, esiste un opzione di default fornita dal compilatore se non ne viene scritta una. Tuttavia come il costruttore copia anche questa si occupa solo di effettuare una duplicazione di ogni membro della classe. Per le classi più semplici, solitamente funziona bene. Comunque quando una classe punta ad alcuni dati membro memorizzati su heap, è consigliabile fornire una propria funzione di assegnazione. Altrimenti si creeranno copie vuote degli oggetti quando vengono assegnati l'un l'altro. Per evitare il problema dare un'occhiata alla funzione scritta per la classe Roditore.

   Roditore& operator= (const Roditore& c)
     { cout << “Overload operatore assegnazione”;
        if (this == &c) return *this;
        else {
                  m_pNome = new string;
                 *m_pNome = c.DaiNome();
                 }
    }
Da notare che la funzione membro restituisce un riferimento all'oggetto Roditore. Per un operatore di assegnazione robusto, deve essere restituito il riferimento all'assegnazione sovraccaricata della funzione membro. Di seguito in main(), sono richiamate le funzioni che verificano l'operatore di assegnazione.

void testAssegnaOp()
{ Roditore ctopo1(“topo1”);
   Roditore ctopo2(“topo2”);
   ctopo1 = ctopo2;
testAssegnaOp() crea due oggetti e ne assegna uno all'altro. La procedura di assegnazione, ctopo1=ctopo2; richiama l'operatore di assegnazione (=) per ctopo1. Nella funzione operator=(), c è una costante di riferimento a ctopo2. Dopo che operator=() viene visualizzato a schermo, viene usato il puntatore this. Di cosa si tratta? E' un puntatore a tutti gli oggetti funzione non statici , al quale punta l'oggetto che viene usato per chiamare la funzione. Nelle righe successive viene confrontato l'indirizzo di ctopo1 sia equivalente a ctopo2 nel caso l'oggetto sia assegnato a se stesso. Altrimenti viene eseguita l'istruzione *m_pNome = c.DaiNome(); che assegna la stringa topo2 al blocco di memoria di ctopo1. Infine si restituisce una copia di ctopo1 restituendo con *this. La verifica viene effettuata facendo DireCiao() a ctopo1 e ctopo2 ed ottenendo il medesimo risultato.