Sunteți pe pagina 1din 4

16.

Funcţii virtuale şi polimorfism

O funcţie virtuală este o funcţie care este declarată ca fiind virutal în clasa de bază şi redefinită de o clasă
derivată. În esenţă, o funcţie virtuală declarată în clasa de bază acţionează ca un substitut pentru păstrarea datelor
care specifică o clasă generală de acţiunii şi declară forma interfeţei. Redefinirea unui funţii virtuale într-o clasă
derivată oferă operaţiile efective pe care le execută funcţia. Altfel spus, o funcţie virtuală defineşte o clasă
generală de acţiuni. Redefinirea ei introduce o metodă specifică.
Când sunt utilizate “normal”, funcţiile virtuale se comportă exact ca oricare altă funcţie membru al clasei.
Însă, ceea ce le face importante şi capabile să admită polimorfismul este modul în care se comportă când sunt
apelate printr-un pointer. Un pointer al clasei de bază poate fi folosit pentru a indica spre orice clasă derivată din
acea bază. când un astfel de pointer indică spre un obiect derivat care conţine o funcţie virtuală, C++ determină care
dintre versiunile funcţiei să fie apelată, în funcţie de tipul obiectului spre care indică acel pointer. Astfel, când sunt
indicate obiecte diferite, sunt executate diferite versiuni ale funcţiei virtuale.

#include<iostream.h>

class BAZA {
public:
virtual void virtual_function() {
cout<<"virtual_fucntion() din BAZA.\n";
}
};
class DERIVAT_1 : public BAZA {
public:
void virtual_function() {
cout<<"virtual_fucntion() din DERIVAT_1.\n";
}
};
class DERIVAT_2 : public BAZA {
public:
void virtual_function() {
cout<<"virtual_function() din DERIVAT_2.\n";
}
};

void main() {
BAZA *p, b;
DERIVAT_1 d1;
DERIVAT_2 d2;

p = &b;
p->virtual_function(); //virtual_function() din BAZA

p = &d1;
p->virtual_function(); //virtual_function() din DERIVAT_1

p = &d2;
p->virtual_function(); //virtual_function() din DERIVAT_2
}

Versiunea de virtual_function() ce se va executa este stabilită de tipul de obiect spre care indică p iar determinarea
aceasta are loc în timpul rulării.

Deşi puteţi apela o funcţie virtuală în modul “obişnuit”

d2.virtual_function(); //apelează virtual_function() din DERIVAT_2

polimorfismul din timpul rulării este permis doar dacă accesul se face print-un pointer al clasei de bază.
Desigur, forma de mai sus nu este greşită, dar nu profitaţi de natura virtuală a clasei de bază.
ATENŢIE: prototipul pentru o funcţie diferă de surpaîncărcarea unui funcţii normale, pentru care tipurile returnate
şi numărul şi tipul parametrilor pot să difere. Când este redefintă o funcţie virtuală, toate caracteristicile
prototipului său trebuie să fie aceleaşi. Dacă modificaţi prototipul atunci când încercaţi să redefiniţi o funcţie
virtuală, compilatorul de C++ o va considera o simplă funcţie supraîncărcată, iar natura sa virtuală se va pierde.

1
RESTRICŢIE: funcţiile virtuale nu trebuie să fie membri de tip static ai clasei din care fac parte. De asemenea, ele
nu pot fi friend. În sfârşit, funcţiile constructor nu pot fi virtuale, în schimb cele destructor pot fi.
N.B. O clasă care include o funcţie virtuală este numită o clasă polimorfică.

Atributul virtual este moştenit

Când o funcţie virtuală este moştenită, se moşteneşte şi natura sa virtuală. O funcţie rămâne virtuală indiferent de
câte ori este moştenită.

Funcţiile virtuale sunt ierarhizate

Când o funcţie este declarată ca fiind virtual într-o clasă de bază, ea poate fi suprascrisă de o clasă derivată. Totuşi,
funcţia nu trebuie neapărat să fie suprascrisă. Dacă o clasă derivată nu suprascrie funcţia virtuală, atunci, când un
obiect din acea clasă derivată are acces la funcţie, este folosită funcţia definită de clasa de bază.

#include<iostream.h>

class B {
public:
virtual void vf() {
cout<<"vf din B\n";
}
};
class D1 : public B {
public:
void vf() {
cout<<"vf din D1\n";
}
};
class D2 : public B {
public:
//vf() nu este surpascrisa de D2, este folosita definitia din baza B
};

void main() {
B *p, b;
D1 d1;
D2 d2;

p = &b;
p->vf();

p = &d1;
p->vf(); //foloseste vf() din baza B

p = &d2;
p->vf();
}

N.B. Atunci când o clasă derivată nu suprascrie o funcţie virtuală, este folosită prima redefinire găsită în ordinea
inversă a derivării.

Funcţii virtuale pure

O funcţie virtuală pură este o funcţie virtuală care nu are definiţie în clasa de bază.

virtual tip nume-functie(lista-de-parametri) = 0;

Când o funcţie virtuală este construită pură, orice clasă derivată trebuie să-i asigure o definiţie. În cazul în care clasa
derivată nu suprascrie funcţia virtuală pură, va rezulta o eroare în timpul compilării.

#include<iostream.h>

2
class NUMAR {
protected:
int val;
public:
void pune_val(int i) {val = i;}
virtual void arata() = 0; //functie virtuala pura
};

class HEX : public NUMAR {


public:
void arata() {cout<<hex<<val<<endl;}
};

class DEC : public NUMAR {


public:
void arata() {cout<<val<<endl;}
};

class OCT : public NUMAR {


public:
void arata() {cout<<oct<<val<<endl;}
};

void main(){
DEC d;
HEX h;
OCT o;

d.pune_val(20);
d.arata(); //20 - zecimal

h.pune_val(20);
h.arata(); //14 - hexazemcimal

o.pune_val(20);
o.arata(); //24 - octal
}

Clase abstracte
O clasă care conţine cel puţin o funcţie virtuală pură se numeşte abstractă.

Utilizarea funcţiilor virtuale

Una dintre cele mai puternice şi mai flexibile căi de introducere a abordării “o interfaţă, metode multiple” este
folosirea funcţiilor virtuale, a claselor abstracte şi a polimorfismului din timpul rulării. Folosind aceste caracteristici,
creaţi o ierarhizare care trece de la general la specific (de la bază la derivat).

#include<iostream.h>

class CONVERT {
protected:
double v1;
double v2;
public:
CONVERT(double i) {v1 = i;}
double daconv() {return v2;}
double dainit() {return v1;}

virtual void calcul() = 0;


};

class LITRI_GALOANE : public CONVERT {


public:

3
LITRI_GALOANE(double i) : CONVERT(i){}
void calcul() {
v2 = v13.7856;
}
};

class FAHRENHEIT_CELSIUS : public CONVERT {


public:
FAHRENHEIT_CELSIUS(double i) : CONVERT(i) {}
void calcul() {
v2 = (v1 - 32)1.8;
}
};

void main() {
CONVERT *p;

LITRI_GALOANE lgob(4);
FAHRENHEIT_CELSIUS fcob(70);

p = &lgob;
cout<<p->dainit()<<" litri -> ";
p->calcul();
cout<<p->daconv()<<" galoane"<<endl;

p = &fcob;
cout<<p->dainit()<<" fahrenheit -> ";
p->calcul();
cout<<p->daconv()<<" celsius "<<endl;
}