Sunteți pe pagina 1din 10

Programare orientată pe obiecte Curs 5

PROGRAMARE ORIENTATĂ PE OBIECTE

Polimorfism
Polimorfismul este unul din conceptele esențiale din POO și reprezintă capacitatea unor entități de
a lua forme diferite (polymorphism), sprijinind astfel scrierea de cod eficient cu costuri de
dezvoltare și întretinere reduse. Această facilitate este specifică limbajelor care permit “legarea
întârziată” la apelul metodelor, adresa de apel a unei metode stabilindu-se doar în momentul
rulării, spre deosebire de cele cu "legare timpurie" la care adresa se stabilește la compilare.

Exemplu

class Figura {
protected:
int latime, inaltime;
public:
void setDimensiune(int latime, int inaltime){
this->latime=latime;
this->inaltime=inaltime;
}
void setDimensiune(int valoare){
this->latime=this->inaltime= valoare;
}
int daArie(){
return 0;
}
void scrie(){
cout<<"Figura"<<endl;
}
};
class Dreptunghi: public Figura{
public:
int daArie(){
return latime*inaltime;
}
void scrie(){
cout<<"Dreptunghi"<<endl;
}
};
class Triunghi: public Figura{
public:
int daArie(){
return latime*inaltime/2;
}
void scrie(){
cout<<"Triunghi"<<endl;
}
};

1
Programare orientată pe obiecte Curs 5

void main(){
Dreptunghi d;
Triunghi t;
Figura *f1 = &d; //atribuire valida
Figura *f2 = &t; //atribuire valida
f1->setDimensiune(1,2); //se apeleaza metoda din clasa de baza
f2->setDimensiune(1,2); se apeleaza metoda din clasa de baza
cout << f1->daArie() << endl;//afiseaza 0
cout << (static_cast<Triunghi*>(f2))->daArie() << endl;//afiseaza 1
}

Important: Dacă în clasa de bază Figura nu ar fi fost declarată metoda daArie atunci, cu toate că
această metodă este declarată în clasele derivate, nu ar fi fost posibile apelurile
f1->daArie()
(static_cast<Triunghi*>(f2))->daArie()

Polimorfismul poate fi de două tipuri:


 Polimorfismul parametric – mecanismul prin care putem defini o metodă cu același nume
în aceași clasă (funcțiile trebuie să difere prin numărul și/sau tipul parametrilor). Selecția
funcției se realizează la compilate –legarea timpurie (early binging).
 Polimorfismul de moștenire – mecanismul prin care o metodă din clasa de bază este
redefinită cu aceiași parametri în clasele derivate. Selecția funcției se va realiza la rulare –
legarea întârziată (late binding, dynamic binding, runtime binding).

În exemplul anterior se observă “inconvenientul” că linia de cod

cout << f1->daArie() << endl;

va determina apelul metodei daArie() descrisă în clasa Figura și nu cea din clasa Dreptunghi.
Pentru a forța lucrul acesta trebuie să declarăm virtuală metoda daArie() din clasa Figura.

Funcții virtuale

O funcție virtuală este o funcție membră a unei clase care va fi înlocuită în momentul execuției
programului cu funcția corespunzătoare din clasa derivată (dacă este declarată). În acest mod
funcțiile virtuale permit o tratare generică (funcție de tipul de dată al obiectului, programul va
determina în timpul execuţiei care funcție se va folosi). Declararea unei funcţii virtuale se face prin
adăugarea cuvântului cheie virtual înaintea prototipului în clasa de bază.

Sintaxă
class IdClasaBaza{

virtual tip idMetodaV(lista_parametri);

};
class IdClasaDerivata : IdClasaBaza{

tip idMetodaV(lista_parametri);

};
2
Programare orientată pe obiecte Curs 5

Cuvântul cheie virtual permite implementarea legăturilor dinamice la apelul funcției prin
intermediul unui pointer al clasei de bază. Mai exact, dacă un pointer al clasei de bază indică către
un obiect al unei clase derivate atunci, la rulare, se va alege versiunea de funcție corespunzătoare
tipului de obiect referit. Dacă avem
IdClasaDerivata d;
IdClasaBaza *p = &d;
atunci
p->idMetodaV();
va apela metoda definită în clasa IdClasaDerivata.
Important: Dacă avem
IdClasaBaza b=d;
atunci
b.idMetodaV();
va apela metoda definită în clasa IdClasaBaza.

Exemplu
Dacă în clasa Figura declarăm virtuală metoda daArie()
class Figura {

virtual int daArie(){
return 0;
}
};

atunci
cout << f1->daArie() << endl; //afiseaza 2
va determina apelul metodei daArie() din clasa Dreptunghi iar
Figura f=d;
cout << f.daArie() << endl;//afiseaza 0
va determina apelul metodei daArie() din clasa Figura.

Utilizarea metodelor virtuale permite tratarea colecțiilor de obiecte ale unei ierarhii de clase într-o
manieră unitară deoarece funcţia apelată virtual este identificată la rulare cu funcţia membră din
clasa căreia îi aparţine obiectul.
Important:
 Constructorii nu pot fi funcții virtuale.
 Destructorii pot fi funcții virtuale (uneori chiar este necesar acest lucru).

Exemplu

class Persoana {
protected:
char *nume;
public:
Persoana(char *nume="") {
this->nume = new char[strlen(nume)+1];
strcpy(this->nume, nume);
}

3
Programare orientată pe obiecte Curs 5

~Persoana() {
if (nume){
delete []nume;
nume = 0;
}
cout << "Distrug un obiect Persoana "<<endl;
}
};

class Angajat : public Persoana {


char * functia;
public:
Angajat(char *nume, char * functia=""): Persoana(nume) {
this->functia = new char[strlen(functia)+1];
strcpy(this->functia, functia);
}
~Angajat() {
if (functia){
delete []functia;
functia = 0;
}
cout << "Distrug un obiect Angajat " << endl;
}
};

void main() {
Persoana* p = new Angajat("Andrei","Director");
delete p;
}

În urma execuției se va afișa:


Distrug un obiect Persoana
Press any key to continue . . .

Dacă în clasa Persoana destructorul este declarat virtual

class Persoana {

virtual ~Persoana() {

}

};

atunci, în urma execuției se va afișa:


Distrug un obiect Angajat
Distrug un obiect Persoana
Press any key to continue . . .

4
Programare orientată pe obiecte Curs 5

Funcții pur virtuale si clase abstracte

Funcțiile virtuale pure sunt funcții virtuale ce sunt doar declarate în clasa de bază, urmând ca
acestea sa fie definite în clasele derivate.

Sintaxă
class IdClasaBaza{
virtual tip idMetodaVirtPura(lista_parametri) = 0;
};

O clasă care conține cel puțin o funcție virtuală pură este denumită clasă abstractă. Datorită
faptului ca aceste clase au cel puțin o metodă doar declarată, nu putem crea instanțe ale claselor
abstracte dar puntem declara pointeri.
Important: Dacă o clasă moștenește o clasă abstractă și nu implementează toate metodele virtuale
pure atunci ea devine clasă abstractă.

Exemplu

class Figura {

virtual int daArie()=0;
};

Clasa Figura devine clasă abstractă si nu mai sunt posibile declarații de genul
Figura f=d; //eroare
ci doar cele cu pointeri
Figura *f1 = &d; //corect

Clase virtuale și moștenirea multiplă

În cazul moștenirii multiple problema care apare (denumită „Problema diamantului”) este aceea
a duplicării datelor membre.

Figura

Dreptunghi Romb

Patrat

5
Programare orientată pe obiecte Curs 5

Exemplu

class Romb:public Figura{


public:
int daArie(){
return latime*inaltime;
}
void scrie(){
cout<<"Romb"<<endl;
}
};

class Patrat: public Dreptunghi,public Romb{


public:
int daArie(){
return latime* inaltime;//eroare
}
void scrie(){
cout<<"Patrat"<<endl;
}
};

Patrat p;
p.setDimensiune(4);//eroare

În clasa Patrat datele membre din clasa Figura vor ajunge de doua ori, prin intermediul clasei
Dreptunghi respectiv prin intermediul clasei Romb. Folosind operatorul “::” putem elimina
ambiguitățile:

class Patrat: public Dreptunghi,public Romb{


public:
int daArie(){
return Dreptunghi::latime*Romb::inaltime;//corect
}
};

p.Dreptunghi::setDimensiune(4);//corect

În urma execuției codului sursă:

Patrat p;
p.Dreptunghi::setDimensiune(4);//corect
p.Romb::setDimensiune(4);//corect
p.scrie();
p.Dreptunghi::scrie();
p.Romb::scrie();
cout << p.daArie() << endl;

se va afișa:
Patrat
Dreptunghi
Romb
16
Press any key to continue . . .

6
Programare orientată pe obiecte Curs 5

Pentru a evita duplicarea datelor membre în cazul moștenirilor multiple, clasa de bază se declară
virtuală în declarațiile claselor derivate, în acest mod datele membru fiind incluse o singură dată.

Exemplu

Dacă declarăm

class Dreptunghi: virtual public Figura{


public:
int daArie(){
return latime*inaltime;
}
void scrie(){
cout<<"Dreptunghi"<<endl;
}
};
class Romb:virtual public Figura{
public:
int daArie(){
return latime*inaltime;
}
void scrie(){
cout<<"Romb"<<endl;
}
};

atunci în clasa Patrat nu vom mai avea duplicate datele moștenite de la clasa Figura. În acest caz
vor fi posibile declarațiile
class Patrat: public Dreptunghi,public Romb{
public:
int daArie(){
return latime*inaltime;//corect
}

};

p.setDimensiune(4);//corect

Important: Constructorul clasei derivate trebuie să apeleze explicit constructorul clasei de bază în
vederea creării copiei unice a datelor.

Exemplu
class Persoana {
protected:
char *nume;
public:
Persoana(char *nume="") {
this->nume = new char[strlen(nume)+1];
strcpy(this->nume, nume);
cout << "Creez un obiect Persoana "<<endl;
}

7
Programare orientată pe obiecte Curs 5

virtual ~Persoana() {
if (nume){
delete []nume;
nume = 0;
}
cout << "Distrug un obiect Persoana "<<endl;
}
};

class Angajat : virtual public Persoana {


char * functia;
public:
Angajat(char *nume, char * functia=""): Persoana(nume) {
this->functia = new char[strlen(functia)+1];
strcpy(this->functia, functia);
cout << "Creez un obiect Angajat "<<endl;
}
~Angajat() {
if (functia){
delete []functia;
functia = 0;
}
cout << "Distrug un obiect Angajat " << endl;
}
};

class Student: virtual public Persoana{


protected:
char facultate[100];
public:
Student(char *nume,char *facultate):Persoana(nume){
strcpy(this->facultate, facultate);
cout << "Creez un obiect Student "<<endl;
}
~Student(){
cout<<"Distrug un obiect Student "<<endl;
}
};

class StudentAngajat: public Student,public Angajat{


public:
StudentAngajat(char *nume, char *facultate,char *functie): Student(nume,facultate),
Angajat(nume, functie),Persoana(nume){
cout << "Creez un obiect StudentAngajat "<<endl;
}
~StudentAngajat(){
cout<<"Distrug un obiect StudentAngajat "<<endl;
}
void print();
};

void main() {
StudentAngajat sa("Teddy", "Informatica","programator");
}

8
Programare orientată pe obiecte Curs 5

În urma execuției se va afișa:


Creez un obiect Persoana
Creez un obiect Student
Creez un obiect Angajat
Creez un obiect StudentAngajat
Distrug un obiect StudentAngajat
Distrug un obiect Angajat
Distrug un obiect Student
Distrug un obiect Persoana
Press any key to continue . . .

Utilizarea operatorului dynamic_cast

O metodă de conversie prin run-time type identification (RTTI) este folosirea operatorului
dynamic_cast. Acesta se foloseşte pentru implementarea conversiilor care au loc în timpul rulării
programului şi pe care compilatorul nu le poate verifica. Acest operator este folosit, de regulă,
pentru downcasting de la un pointer la clasa de bază către un pointer la clasa derivată. El poate fi
folosit doar pentru pointeri sau referinţe la obiecte.

Important: Operatorul dynamic_cast poate fi folosit doar cu clase în care cel puțin o funcție este
virtuală.
Expresia
dynamic_cast <T> ( v )

va converti v la tipul T. T trebuie să fie un pointer sau o referință. Dacă T este un pointer și
conversia nu poate fi facută atunci operatorul va întoarce un pointer null de tip T. Dacă T este o
referință și conversia nu se poate realiza operatorul va arunca o excepție std::bad_cast (clasă
din libraria standard <typeinfo>).

Exemplu de utilizare a operatorului dynamic_cast:

void daTip(Figura* fig) {


Dreptunghi* d = dynamic_cast<Dreptunghi*>(fig);
Triunghi* t = dynamic_cast<Triunghi*>(fig);
Romb* r = dynamic_cast<Romb*>(fig);
Patrat* p = dynamic_cast<Patrat*>(fig);

if (d)
cout<<"Este un element din clasa Dreptunghi\n";
else if(t)
cout<<"Este un element din clasa Triunghi\n";
else if(r)
cout<<"Este un element din clasa Romb\n";
else
cout<<"Este un element din clasa Patrat\n";
};

void main() {
Dreptunghi d;
Triunghi t;
daTip(&d);
daTip(&t);
}

9
Programare orientată pe obiecte Curs 5

În urma execuției se va afișa:


Este un element din clasa Dreptunghi
Este un element din clasa Triunghi
Press any key to continue . . .

10

S-ar putea să vă placă și