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;
}
In questo blog si parla di programmazione con il linguaggio C++, i post cercheranno di essere molto esplicativi sull'argomento trattato.
Visualizzazione post con etichetta assegnazione. Mostra tutti i post
Visualizzazione post con etichetta assegnazione. Mostra tutti i post
sabato 26 gennaio 2013
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.
Iscriviti a:
Post (Atom)