| Autor |
Mesaj |
Admin
Administrator

Data inscrierii: 07/Dec/2013
Mesaje: 1103
|
• Functiile virtuale sunt necesare in reimplementarea unor functii membre ale unor clase de baza atat static cat si dinamic.
• O functie virtuala este o functie a carei declaratie este precedata de cuvantul cheie virtual.
Sintaxa declaratiei:
virtual tip_retur id_functie (lista_par_f) ;
• Functia virtuala se declara in clasa de baza si este redefinita de o clasa derivata Redefinirea unei functii virtuale intr-o clasa derivata pune in evidenta operatiile efective pe care le executa functia.
tip_retur id_functie(lista_par);
• O functie membra definita ca functie virtuala intr-o clasa de baza ramane virtuala in toate clasele derivate de la aceasta clasa de baza.
Exemplu:
class Punct
{
public:
float x,y;
void punctul(float x0, float y0);
virtual float Aria( ){return 0.0;}
//definirea functiei virtuale in clasa de baza
};
class patrat:public Punct
{
public:
float l4;
float Aria( ){return l4*l4;}
// functia virtuala in clasa derivata
};
void main( )
{
Punct p;
patrat pt;
pt.l4(15.2);
Punct *pp;
pp=&p;
float ap=pp->Aria( );
pp=&pt;
float apt=pp->Aria( );
………………………..
}
• Se stie ca polimorfismul este admis de C++ atat in faza de compilare cat si in cea de executie. Dupa cum am mai precizat, polimorfismul in momentul compilarii este pus in evidenta prin functii si operatori supraincarcati [L10, L11, L12], iar polimorfismul in timpul executiei se realizeaza pe baza mostenirii si functiilor virtuale.
• In mod normal, functiile virtuale se comporta la fel ca orice alta functie membra a clasei respective. Capabilitatea lor de a accepta polimorfismul reprezinta modul lor de comportare cand sunt apelate printr-un pointer. Un pointer al clasei de baza poate fi întrebuințat pentru a indica orice clasa derivata din acea clasa de baza. Daca un astfel de pointer indica spre un obiect derivat care contine o functie virtuala, atunci C++ determina care dintre versiunile functiei sa fie apelata (in functie de tipul obiectului pointat). Cand sunt indicate obiecte diferite, sunt executate distincte versiuni ale functiei virtuale.
Exemplu:
…………………………………..
class B
{
public:
virtual void vf( )
{
cout<<"vf este o functie virtuala in clasa de baza B \n";
}
};
class D_1:public B
{
public:
void vf ( ){cout<<” functia virtuala vf() in clasa derivata D_1 \n";}
};
class D_2:public B
{
public:
void vf( ){cout<<" functia virtuala vf() in clasa derivata D_2 \n";}
};
main( )
{
B *p,b;
// se declara un pointer si un obiect al clasei de baza B
D_1 d_1;
// se declara un obiect al clasei derivate D_1 (instantiere)
D_2 d_2;
// idem pentru clasa D_2
p=&b;
// p pointeaza la clasa B, atribuindu-se ca valoare adresa obiectului b din B
p->vf( );
// p acceseaza functia virtuala vf() din clasa de baza B
…………….
p=&d_1;
// p pointeaza catre clasa derivata D_1
p->vf( );
// acces la vf() din clasa derivata D_1
…………………..
p=&d_2;
// p pointeaza spre clasa derivata D_2
p->vf( );
// acces la vf( ) din clasa derivata D_2
return 0;
}
Se observa ca selectarea versiunii functiei virtuale vf( ), care se va executa, este stabilita de tipul obiectului spre care pointeaza pointerul p.
• Desi se poate apela o functie virtuala in mod obisnuit întrebuințând identificatorul obiectului si operatorul de accesare ".", polimorfismul in timpul rularii este permis doar daca accesul se face printr-un pointer al clasei de baza. Cu alte cuvinte, daca folosim sintaxa
d_1.vf();
se va apela functia vf( ) din clasa derivata D_1, dar nu se va mai profita de avantajul functiei virtuale vf( ).
• Redefinirea unei functii virtuale pastreaza identic prototipul sau, deci nu este vorba despre o supraincarcare a functiei respective [L10]. Legaturile unei functii virtuale cu un obiect al clasei derivate se realizeaza in mod dinamic (in timpul executiei) si nu static.
Exemplu:
#include<iostream>
class dist
{
public:
double d;
dist(double l){d=l;}
virtual void time( )
{
cout<<"Viteza mobilului este de 60 km/h\n";
cout<<"Timpul necesar:"<<d/60<<"\n";
} // Ce se intampla daca nu ar fi functie virtuala?
};
class dist_fin:public dist
{
public:
dist_fin(double d):dist(d){ }// explicati rezultatul obtinut
void time( )
{
cout<<"Viteza de deplasare este de 100km/h\n";
cout<<"Timp necesar:"<<d/100<<"\n";
}
};
void main( )
{
dist *p,O(100.0);
dist_fin OB(100.0);
p=&O;
p->time( );
p=&OB;
p->time( );
}
• Caracteristici ale unei functii virtuale:
1) O functie virtuala nu poate fi functie membra statica a clasei de baza respective.
2) O functie virtuala nu poate fi functie friend.
3) Functiile constructor nu pot fi functii virtuale.
4) Functiile destructor pot fi functii virtuale.
• O clasa care contine o functie virtuala se mai numeste si clasa polimorfica.
• O functie virtuala isi pastreaza acest atribut indiferent de cate ori este mostenita. Asadar, daca o clasa derivata, care a mostenit de la clasa de baza o functie virtuala,devine o clasa de baza pentru o alta clasa derivata, functia virtuala poate fi in continuare redefinita. Functia virtuala se poate redefini intr-o ierarhie de forma: clasa_baza->clasa_derivata....
Exemplu:
…………………………………..
class B
{
public:
virtual void vf( )
{
cout<<"vf este o functie virtuala in clasa de baza B \n";
}
};
class D_1:public B
{
public:
void vf( )
{cout<<"redefinirea functiei virtuale vf() in clasa derivata D_1 \n"; }
};
class D_2:public D_1
{
public:
void vf( ){cout<<"mostenirea functiei vf() in clasa derivata D_2 \n";}
};
main( )
{
B *p,b;
// se declara un pointer si un obiect al clasei de baza B
D_1 d_1;
// se declara un obiect al clasei derivate D_1 (instantiere)
D_2 d_2;
// idem pentru clasa D_2
p=&b;
// p pointeaza spre clasa B, atribuindu-se ca valoare adresa obiectului b din clasa B
p->vf( );
// p acceseaza functia virtuala vf() din clasa de baza B
…………….
p=&d-1;
// p pointeaza spre clasa derivata D_1
p->vf( );
// acces la vf() din clasa derivata D_1
…………………..
p=&d_2;
// p pointeaza spre clasa derivata D_2
p->vf( );
// acces la vf() din clasa derivata D_2
return 0;
}
• Daca o clasa derivata dintr-o clasa de baza, nu redefineste functia virtuala prezenta in clasa de baza, atunci, cand un obiect din clasa derivata solicita acces la functia respectiva, se va folosi functia definita in clasa de baza.
Exemplu:
…………………………………..
class B
{
public:
virtual void vf( )
{
cout<<"vf este o functie virtuala in clasa de baza B \n";
}
};
class D_1:public B
{
public:
void vf( )
{cout<<"aceasta este functia vf( ) din clasa derivata D_1 \n";}
};
class D_2:public B
{
public:
// vf( ) nu este redefinita de clasa derivata D_2 si va fi folosita definitia din clasa //B
};
main( )
{
B *p,b;
// se declara un pointer / un obiect al clasei de baza B
D_1 d_1;
// se declara un obiect al clasei derivate D_1 (instantiere)
D_2 d_2;
// idem pentru clasa D_2
p=&b;
// p pointeaza spre clasa B, atribuindu-se ca valoare adresa obiectului b din clasa //B
p->vf( );
// p acceseaza functia virtuala vf( ) din clasa de baza B
…………….
p=&d-1;
// p pointeaza spre clasa derivata D_1
p->vf( );
// acces la vf( ) din clasa derivata D_1
…………………..
p=&d_2;
// p pointeaza spre clasa derivata D_2
p->vf( );
// acces la functia vf( ) din clasa de baza B
return 0;
}
• Mai exact, in C++ mostenirea este ierarhizata. Accesul la o functie virtuala, pentru un obiect al unei clase derivate in care nu s-a facut redefinirea functiei virtuale respective, consta in accesarea primei redefiniri a sa in ordinea inversa derivarii.
Exemplu:
#include<iostream>
class rad
{
public:
virtual void afis( ){cout<<"Nu exista ascendenti!\n";}
};
class St:public rad
{
public:
void afis( ){cout<<"Exista descendent stanga\n";}
};
class Dr:public St
{
public:
void afis( ){cout<<"Exista un descendent dreapta al precedentului nod\n";}
};
void main( )
{
//Dati explicatii asupra mesajelor care se vor afisa
St n;
n.afis( );
Dr n_1;
n_1.afis( );
}
• Corelarea intre functii virtuale si alte functii virtuale sau nonvirtuale:
1) Functile virtuale nu pot fi supraincarcate [L10] dar pot apela astfel de functii.
2) Functiile virtuale pot apela functii care nu sunt virtuale si invers.
3) Functiile virtuale pot apela alte functii virtuale.
Exemplu:
# conține <iostream>
class Inf_1
{
public:
virtual void adevarul ( )
{ cout<<"Excursie in Romania:\n"; supliment_adev( );}
void supliment_adev( )
{cout<<"Este o tara cu un potential turistic \n"; supl_turist( );}
virtual void supl_turist( ){cout<<"Relieful ei este variat \n"; supl_econ( );}
virtual supl_econ( ){cout<<"Bogatiile subterane sunt diverse \n"; supl_fin( );}
void supl_fin( ){ cout<<"Optimism \n";}
};
class Inf_2:public Inf_1
{
public:
void supl_turist( ){cout<<"Relieful ei este monoton:\n"; supl_econ( );}
void supl_econ ( ){cout<<"Apele curg furios \n"; supl_fin( );}
void supl_fin( ){cout<<"Pesimism\n";}
};
void main( )
{
char c;
Inf_1 * mesaj;
cout<<"Care varianta va intereseaza (O/P)?\n";
cin>>c;
if((c=='O') || (c=='1'))
mesaj=new Inf_1;
else
mesaj=new Inf_2;
mesaj->adevarul();
delete mesaj;}
• Pentru a intelege modul de utilizare a functiilor virtuale, trebuie inteles modul de implementare al lor. Se stie ca fiecare functie membra are atasat pointerul this. In cazul functiilor virtuale se utilizeaza o tabela de pointeri catre functii, care se mai numeste si tabela de functii virtuale. Fiecare clasa, care poseda o functie virtuala, are atasata o astfel de tabela. Tabela are o intrare pentru fiecare functie virtuala, incluzand si pe cele mostenite de la clasa de baza. O astfel de intrare consta dintr-un pointer care pointeaza catre functia apelata de functia virtuala corespunzatoare.
• Un obiect al unei clase, care contine functii virtuale, poseda un pointer memorat in el prin care acceseaza tabela virtuala a clasei respective. De fiecare data, cand este apelata o functie virtuala pentru acel obiect, pointerul va localiza, in tabela, functia corespunzatoare.
In exemplul anterior se poate observa urmatoarea conexiune:
[center]Inf_1[/center][center][table border="1"][tr][td](*adevărul)( )[/td][td](*supl_turist)( )[/td][td](*supl_econ)( )[/td][/tr][tr][td]void Inf_1::adevărul( ){…}[/td][td]void Inf_1::supl_turist( ){…}[/td][td]void Inf_1::supl_econ( ){…}[/td][/tr][/table][/center]
[center]Inf_2[/center][center][table border="1"][tr][td](*adevărul)( )[/td][td](*supl_turist)( )[/td][td](*supl_econ)( )[/td][/tr][tr][td]void Inf_1::adevărul( ){…}[/td][td]void Inf_2::supl_turist( ){…}[/td][td]void Inf_2::supl_econ( ){…}[/td][/tr][/table][/center][center]Tabele virtuale[/center]
Ambele tabele contin acelasi pointer catre adevarul( ), iar supliment_adev( ) si supl_fin( ) nici nu apar.
• De regula, asa cum am mai precizat, clasa de baza a unei ierarhii trebuie sa fie cat mai generala, elementele specifice urmand a se adauga la derivarile acesteia. Gradul de generalitate al clasei de baza poate ajunge pana acolo, incat ea nici sa nu mai intervina in crearea obiectelor. Astfel de clase se numesc clase abstracte.
• Am vazut, ca daca o clasa derivata nu redefineste functia virtuala din clasa de baza corespunzatoare, atunci se va folosi versiunea prezenta in clasa de baza. Exista multe cazuri in care o clasa de baza nu poate defini suficient un obiect pentru a îngăduie sa fie creata o functie virtuala in acea clasa. Altfel zis, este posibil sa nu existe o definitie semnificativa a functiei virtuale in clasa de baza. Un astfel de deziderat se poate inlatura in C++ utilizând functii virtuale pure.
• Functia virtuala, care nu poseda definitie in clasa de baza, se numeste functie virtuala pura.
Sintaxa declararii:
virtual tip_f id_functie(lista_par)=0;
• Constructorii si destructorii nu pot fi functii virtuale pure.
• In cazul unei functii virtuale pure, orice clasa derivata va necesită sa o defineasca.
• O clasa abstracta contine cel putin o functie virtuala pura.
Exemplu:
# include <iostream>
class baza
{
protected:
int nr;
public:
void cit_nr(int m){nr=m;}
virtual void afis( )=0; //functie virtuala pura
};
class tip_h:public baza
{
public:
void afis( ){cout<<hex<<nr<<\n";}
};
class tip_o:public baza
{
public:
void afis( ){cout<<oct<<nr<<"\n";}
};
main( )
{
tip_h n1;
tip_o n2;
n1.cit_nr(50);
n1.afis( );
n2.cit_nr(50);
n2.afis( );
return 0;
}
Functia afis( ) fiind declarata ca functie vituala pura suntem asigurati de faptul ca toate clasele derivate o vor redeclara ca functie pura sau o vor defini..
• Deoarece o clasa abstracta contine functii, pentru care nu exista definitii (functii virtuale pure), nu se pot crea obiecte ale sale. Totusi se pot crea pointeri si referinte catre aceste clase. De aceea clasele abstracte admit polimorfismul in timpul executiei, care consta in alegerea functiei virtuale corespunzatoare de catre pointerii clasei de baza.
• In C++, o clasa de baza poate fi folosita pentru a defini natura unei interfete cu o clasa generala. Fiecare clasa derivata va introduce operatiile specifice solicitate de tipurile de date folosite de tipul derivat.
• Definind o clasa abstracta, vom reusi sa stabilim un mod unic de comunicare cu utilizatorul. Unui astfel de mod de comunicare ii va fi atasat un set de metode (functii membre) caracteristice claselor derivate. De aceea, se mai spune, ca utilizarea functiilor virtuale, a claselor abstracte si a polimorfismului in timpul executiei reprezintă cele mai puternice si flexibile cai de a obtine o interfata unica cu metode multiple. Cu aceasta se realizeaza o ierarhizare de la general la specific.
Aplicatie:
Sa consideram ierarhia de clase: punct_2D (clasa de baza) si punct_3D (clasa derivata). Analizati aplicatia respectiva si precizati unde s-au exploatat corect functiile virtuale si in ce situatii ele si-au pierdut calitatea de functii virtuale.
#include<iostream> #include<math> #include<conio>
class punct_2D
{ private:
float x,y;
void init_2D(float a=0,float b=0)
{x=a;y=b;}
protected:
float r_2D,teta;
float modul()
{return sqrt(x*x+y*y);
}
float TETA()
{
if(x)return atan(y/x);
if(y>0)return M_PI/2;
return -M_PI/2;
}
void afis_x()
{
cout<<"x="<<x<<"\n";
}
void afis_y()
{
cout<<"y="<<y<<"\n";
}
float ret_x(){return x;}
float ret_y(){return y;}
void afis_r_2D()
{
cout<<"r_2D="<<r_2D<<"\n";
}
public:
punct_2D(float a,float b)
{
init_2D(a,b);
r_2D=modul();
teta=TETA();
}
punct_2D()
{
init_2D();
r_2D=teta=0;
}
virtual void afis()
{
afis_x(); afis_y();
afis_r_2D();
}
virtual void transl(float dx,float dy);
};
void punct_2D::transl(float dx,float dy)
{
x+=dx;
y+=dy;
r_2D=modul();
teta=TETA();
}
class punct_3D:public punct_2D
{
private:
float z;
void init_3D(float c=0){z=c;}
protected:
float r_3D,f;
float modul(){
return (pow(punct_2D::modul(),2)+pow(z,2));
}
float F()
{
if(r_2D)return atan(z/r_2D);
if(z>0)return M_PI/2;
return -M_PI/2;
}
public:
void afis()
{
punct_2D::afis_x();
punct_2D::afis_y();
cout<<"z="<<z<<"\n";
cout<<"r_3D="<<r_3D<<"\n";
cout<<"f="<<f<<"\n";
}
void transl(float dx,float dy,float dz)
{
punct_2D::transl(dx,dy);
z+=dz;
r_3D=modul();f=F();}
punct_3D():punct_2D(0,0)
{
init_3D();
r_3D=f=0;
}
punct_3D(float a,float b,float c):punct_2D(a,b)
{
init_3D(c);
r_3D=modul();
f=F();}
void rotatie_oz(int u)
{
float x,y;
x=punct_2D::ret_x();
y=punct_2D::ret_y();
x=x*cos(u*M_PI/180)-y*sin(u*M_PI/180);
y=x*sin(u*M_PI/180)+y*cos(u*M_PI/180);
}
};
void main()
{
punct_2D xx,yy(11,12),*p;
xx.afis();
p=&yy;
p->afis();
punct_3D g,h(13,23,32),*q;
clrscr();
g.afis();
q=&h;
q->afis();
q->transl(45,55,65);
q->afis();
q->rotatie_oz(74);
q->afis();getch();} |
|
|
|
|
 |
|
|
|
|
Subiectul urmator
Subiectul anterior
Nu puteti crea un subiect nou in acest forum Nu puteti raspunde in subiectele acestui forum Nu puteti modifica mesajele proprii din acest forum Nu puteti sterge mesajele proprii din acest forum Nu puteti vota in chestionarele din acest forum |
Powered by phpBB
© 2001, 2002 phpBB Group
|