Newsgroup: it.comp.lang.c++
Titolo: Funzioni virtuali

Premetto che sono mooolto nuovo al C++ (inteso come programmazione ad 
oggetti) e che sono autodidatta.
Non ho capito bene a cosa servono le funzioni virtuali e da cosa si 
differenziano rispetto alle normali funzioni membro.
Grazie


Molti rispettabili autori nonche' guru del C++, hanno dedicato
centinaia di righe per cercare di spiegare il significato,
il meccanismo e l'importanza delle funzioni virtuali.
Il mio e' un tentativo, quindi, nel darti una spiegazione.
Con le funzioni virtuali, il linguaggio C++ mette in pratica
il concetto di polimorfismo e di ereditarieta' e per toccare
con mano gli effetti elementari di questi meccanismi e'
necessario fare uso di puntatori.
//-------------------------------------------------------------
//
//Semplice esempio per spiegare il meccanismo delle funzioni
//virtuali
//
//Osvaldo Ciurleo - 2 Mar 98
//
//-------------------------------------------------------------
#include <iostream.h>
#include <conio.h>
//----------------------------------------------[class Base]---
//
//Una striminsita classe base con una funzione normale ed una 
//virtuale
//
class Base
{
public:
	void		NonVirtuale( ) { cout << "Base" << endl; }
	virtual void	Virtuale( ) { cout << "Base" << endl; }
};
//-------------------------------------------[class Derived]---
//
//La classe Derived e' ereditata da Base
//
class Derived : public Base
{
public:
	void		NonVirtuale( ) { cout << "Derived" << endl; }
	virtual void	Virtuale( ) { cout << "Derived" << endl; }
};
//-------------------------------------------[Print(Base* B)]---
//
// La funzione Print riceve un puntatore ad un oggetto Base.
// Al suo interno vengono chiamate le funzioni membro della
// classe. Print non sa quale oggetto sara' passato. Esso infatti
// potra' essere di tipo Base o qualunque di una classe derivata 
// da Base. Il comportamento sara' stabilito a run-time, in base al
// tipo di oggetto che sara' passato
//
void Print(Base * B)
{
	cout << "B->NonVirtuale()\n";
	B->NonVirtuale();
	cout << endl;
	cout << "\nB->Virtuale()\n";
	B->Virtuale();
	cout << endl;
}
int main()
{
	//Alloco sullo stack un oggetto di tipo Base
	//
	Base	objB;
	//Alloco sullo stack un oggetto di tipo Derived
	//
	Derived objD;
	//un puntatore alla classe Base
	//
	Base* pB;
	//A questo punto il puntatore punta l'oggetto
	//Base objB
	//
	pB = &objB;
	//Print chiamera' le funzioni membro della classe
	//Base
	//
	Print(pB);
 
	//Adesso il puntatore ottiene l'indirizzo dell'oggetto
	//Derived objD
	//
	pB = &objD;
	//In questo caso Print ha un comportamento polimorfico
	//Infatti il puntatore e' di tipo Base, ma punta un
	//oggetto di tipo Derived. Poiche' in Derived c'e'
	//una funzione che ha lo stesso nome di quella presente
	//nella classe madre, ma dichiarata virtual, consertira'
	//al compilatore di chiamare la funzione della classe
	//Derived che ha un comportamento diverso.
	//
	Print(pB);
	cout.flush();
	getch();
	return 0;
}
//---------------------------------------------------------
Le funzioni viruali hanno lo scopo di garantire la chiamata
alla funzione giusta appartenente alla classe giusta.
Esse devono avere lo stesso nome cosi' che si possano
garantire la stessa interfaccia, ovvero lo stesso nome
ma un comportamento diverso a seconda che appartenga ad una
o all'altra classe gerarchicamente collegata.
Vi sono altri aspetti importanti che meritano particolare
attenzione, ma dovrei scrivere un capitolo sull'argomento.
Poiche' non sono un bravo scrittore... mi fermo qui.
Spero di esserti stato di aiuto.
Buona fotuna.

 wrote:
All'ottima spiegazione aggiungo che di solito le funzioni virtuali si 
usano quando si vuole preconfezionare un'interfaccia per l'utente 
della propria classe.
Ad esempio nella programmazione ad oggetti di Windows, in 
Objectwindows della Borland sono dichiarate virtual tutte le funzioni 
di risposta ai messaggi di Windows, cioe' per intenderci agli eventi 
che l'utente genera spostando il mouse ecc..
Il programmatore in OWL  puo' definire nella classe propria derivata 
come rispondere ai messaggi, ma il prototipo della funzione resta 
sempre lo stesso, in questa maniera in una gerarchia di classi 
complessa e' possibile passare puntatori alla classe base per accedere
alle funzioni di interfaccia delle classi derivate, perche' il 
prototipo della funzione e' noto sin dalla classe base, cosa molto 
utile in diverse situazioni per evitare contorsionismi.
<< The show must go home ! >>
** Email replies to 
md7...@mclink.it
 **
visit my homepage at: 
http://www.volftp.tin.it/IT/IT/ITALIANI/rullo/index.htm