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

Ciao,
chiedo cortesemente a qualche buona anima se mi spiega il significato delle
funzioni virtuali...
in molti esempi trovo che il distruttore di alcune classi viene preceduto
dalla parola virtual...
tipo
virtual ~CProdDlg();
ma se scrivo
~CProdDlg();
cosa cambia?
grazie
Davide

 ha scritto nel messaggio
news:fakm71-...@zarathustra.starfleet.com...


 funzioni virtuali...
La comprensione delle funzioni virtuali e della keyword 'virtual' in C++
presuppone la conoscenza della nozione di 'polimorfismo'. Ora, essendo
questa una delle nozioni fondamentali della OOP (direi, anzi, la piů
importante in assoluto), mi pare sia opportuno che venga appresa su un
manuale, che normalmente la tratta in maniera piů adeguata di quanto si
possa (o si voglia) fare in un newsgroup.
Max

Max M. wrote:

Alias RTFM ?
Vediamo se riusciamo in maniera adeguata in poche parole a fare un
esempio di cosa voglia dire "virtuale".
Faccio esempi non compilabili ma che dovrebbero spiegare il concetto:
============  ESEMPIO-1
struct Base
{
     void exec() { std << "Base::exec" << endl; }
};
struct Derivata : public Base
{
     void exec() { std << "Derivata::exec" << endl; }
};
foo( Base& aBase)
{
    aBase.exec();
}
Derivata myDerivata;
foo(myDerivata);
qui la exec non e' virtuale, quando chiami la foo passando un'istanza
di Derivata, eseguendo la exec vedi stampato:    Base::exec.
Questo perche' non avendo specificato exec virtual non avviene quello
che viene chiamato "dynamic binding".
============  ESEMPIO-2
struct Base
{
     virtual void exec() { std << "Base::exec" << endl; }
};
struct Derivata : public Base
{
     virtual void exec() { std << "Derivata::exec" << endl; }
};
foo( Base& aBase)
{
    aBase.exec();
}
Derivata myDerivata;
foo(myDerivata);
qui la exec e' virtuale, quando chiami la foo passando un'istanza
di Derivata, eseguendo la exec vedi stampato:    Derivata::exec.
Qui avviene il "dynamic binding".
Nota che in entrambi gli esempi la foo prende l'argomento per
reference, questo perche il binding dinamico viene fatto solo se
maneggi puntatori ( la reference in c++ e' zucchero sintattico ).
Ora il distruttore virtuale ha un altro motivo di esistere:
supponi che la classe Derivata nel suo costruttore allochi memoria
dinamica con una new e come ben sai questa memoria deve essere liberata
non appena l'istanza della classe "muore"
Se tu non avessi dichiarato il distruttore di Base virtuale
nel seguente codice
Base *myBase = new Derivata();
....
delete myBase;
avresti che il distruttore di Derivata non verrebbe mai chiamato con
conseguente memory leakage.
La regola per il distruttore e' questa, se hai intenzione di far
derivare la classe che scrivi allora il distruttore va fatto virtuale,
se invece non desideri che venga estesa puoi anche risparmiarti di
fare virtuale il distruttore ( che non e' gratis averlo ).
Gaetano

 conseguente memory leakage.
Questo non è in realtà corretto. Per lo Standard l'effetto di 
questo codice (in assenza di un distruttore virtuale) è assolutamente 
*indefinito*. Con il GCC, ad esempio, è sufficiente aggiungere un po' di 
derivazione multipla, e l'assenza di un distruttore virtuale ha 
conseguenze disastrose molto visibili (leggi Segmentation fault):
 struct A { int a; };
 struct B { int b; };
 struct C: public A, public B { }; // no virtual dtor
 int main()
 {
       B* p = new C;
       delete p;    // crash!
 }
Max

 *indefinito*. 
Anche.