Documente Academic
Documente Profesional
Documente Cultură
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()
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;
}
};
void main() {
Persoana* p = new Angajat("Andrei","Director");
delete p;
}
class Persoana {
…
virtual ~Persoana() {
…
}
…
};
4
Programare orientată pe obiecte Curs 5
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
Î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
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:
p.Dreptunghi::setDimensiune(4);//corect
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
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;
}
};
void main() {
StudentAngajat sa("Teddy", "Informatica","programator");
}
8
Programare orientată pe obiecte Curs 5
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>).
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
10