In cadrul acestui laborator, vom aprofunda inca un concept important al Programarii Orientate pe Obiecte, functii virtuale.
Ca referinte externe, recomandam urmatorul capitol din Absolute C++:
Pentru a intelege mai bine importanta folosirii functiilor virtuale, introducem conceptul de binding(legare).
Legarea poate fi:
Legarea statica (Early binding):
Asa cum sugereaza si numele, compilatorul(sau linker-ul) asociaza in mod direct apelului de functie o adresa.
Functiile membre ale unei clase primesc adresa obiectului care face apelul.
tip_date obiect; obiect.functie();
In functie de tipul obiectului de la acea adresa - compilatorul si editorul de legaturi stabilesc daca:
In cazul apelului prin intermediul pointerilor.
tip_date_baza *obiect = new tip_date_baza; obiect->functie();
Procedeul este similar:
#include<iostream> using namespace std; class Baza { public: void afisare() { cout<<" In Baza \n"; } }; class Derivata: public Baza { public: void afisare() { cout<<"In Derivata \n"; } }; int main(void) { Baza *bp = new Derivata; // Compilatorul vede tipul pointerului si // apeleaza functia din clasa Baza bp->afisare(); return 0; }
In urma rularii programului, se va afisa:
In Baza
((Derivata*)bp)->afisare();
Legare dinamica/tarzie(Late binding):
Pentru a folosi acest tip de legare, compilatorul trebuie informat ca exista posibilitate ca, la rulare, sa se doreasca apelarea unei functii de tipul dinamic al obiectului.
#include<iostream> using namespace std; class Baza { public: virtual void afisare() { cout<<" In Baza \n"; } }; //legarea pentru functia afisare se face la rulare class Derivata: public Baza { public: void afisare() { cout<<"In Derivata \n"; } //supraincarcare }; //legarea pentru functia afisare se face la rulare; e virtuala int main(void) { Baza *bp = new Derivata; //legare dinamica (in functie de tipul dinamic), apel Derivata::afisare() bp->afisare(); return 0; }
Observam asadar ca de data aceasta, se va afisa
In Derivata
Dupa cum am afirmat deja, cuvantul cheie virtual informeaza compilatorul sa nu realizeze o legatura statica. Dar cum reuseste acesta sa puna in actiune toate mecanismele necesare realizarii de legaturi dinamice?
In VTABLE sunt puse adresele functiilor virtuale ale acelei clase. Fiecare obiect de tipul clasei cu functii virtuale va avea in posesie un pointer numit vpointer (sau VPTR) catre adresa lui VTABLE.
Cand se face initializarea VPTR-ului?
In momentul crearii unui obiect. Mai exact la apelul constructorului. In cazul in care nu se implementeaza niciun constructor, cel generat automat face si aceasta initializare.
Polimorfismul se poate realiza prin:
★Pentru ca legarea dinamica nu este la fel de eficienta ca legarea statica (asa ca o folosim doar cand e nevoie).
Cand folosim functii virtuale atunci?
★De fiecare data cand vrem ca in clasa derivata sa modificam/adaptam/suprascriem comportamentul unei functii deja implementate in clasa de baza.
Cand este important acest mecanism?
#pragma once #include <iostream> using namespace std; class Baza{ protected: int atr1; public: Baza(); Baza(int ); void set_atr1(int ); virtual void afisare(); virtual ~Baza(){}; };
#include "Baza.h" Baza::Baza() { } Baza::Baza(int i):atr1(i) { } void Baza::set_atr1(int i) { atr1 = i; } void Baza::afisare() { cout << "atr1 = " << atr1 << endl; }
#pragma once #include "Baza.h" class Derivata : public Baza { protected: int atr2; public: Derivata(); Derivata(int , int ); void set_atr2(int ); void afisare(); //afisare din Derivata e virtuala };//destructorul generat automat e virtual
#include "Derivata.h" Derivata::Derivata() { } Derivata::Derivata(int a1, int a2):Baza(a1),atr2(a2) { } void Derivata::set_atr2(int n) { atr2 = n; } void Derivata::afisare() { Baza::afisare(); cout << "atr2 = " << atr2 << endl; }
#include "Derivata.h" int main(int argc, char *argv[]) { int n; cout << "Dati dimensiunea"; cin >> n; Baza **vec = new Baza*[n]; for (int i = 0; i < n; i++){ cout << "Introduceti obiect de tip Baza(0) sau Derivata(1)?"; int tip; cin >> tip; if (tip == 0){ cout << "Dati atr1:"; int a1; cin >> a1; vec[i] = new Baza(a1); } else if (tip == 1){ cout << "Dati atr1 si atr2:"; int a1, a2; cin >> a1; cin >> a2; vec[i] = new Derivata(a1,a2); } else i--; } for (int i = 0; i < n; i++) vec[i]->afisare(); //comportament polimorf; nu mai testez eu care e tipul si nu fac conversii explicite //Baza *b = new Derivata(1,1); // b->set_atr2(2); //atentie, set_atr2 nu e o functie din Baza //ERROR:'class Baza' has no member named 'set_atr2' return 0; }