Supraincarcarea (overloading), prin redefinirea unor functii sau metode, permite alegerea la compilare-link_editare a functiei sau a metodei dorite prin semnatura acesteia, fara a mai putea alege la executie.
Polimorfismul permite ca la executie sa se decida ce metoda sa fie apelata, oferind o facilitate a metodelor din clase aflate in relatie de derivare. Prin polimorfism se executa actiuni diferite prin mesaje cu semnaturi identice asupra obiectelor de tip diferit (obiecte din clase diferite raspund diferit la acelasi mesaj).
Doua obiecte sunt compatibile daca apartin aceleasi clase (evident) dar si doua variabile
a) de tip pointer la clasa de baza, respectiv pointer la clasa derivata,
b) de tip referinta (pointer constant) la clasa de baza, respectiv referinta la clasa derivata .
Metodele unei clase pot fi:
Clasice - metode legate static, la compilare-link_editare fiind fixata adresa de apel a metodei, fara posib 555e47f ilitatea de a o schimba la rularea aplicatiei,
Polimorifice metode legate dinamic, care permit intarzierea deciziei referitoare la adresa de apel a metodei, pana la executie.
Legarea unei metode (binding), intelegand prin aceasta cenexiunea logica dintre o entitate si o proprietate a acesteia (corespondenta dintre un mesaj trimis unui obiect, adica ordinul de apel, si metoda care se executa ca raspuns la acesta) poate fi:
Timpurie (statica - early-binding) - compilatorul si editorul de legaturi vor fixa adresa metodei care se executa, fara ca aceasta sa mai poata fi modificata pe parcursul executiei;
Tarzie (dinamica - late-binding) - compilatorul va construi un tablou de adrese ale metodelor posibile de apel, iar determinarea adresei metodei dorite se va efectua doar la executie. In functie valoarea pointerului spre clasa de baza, care poate contine si adresa unui obiect al clasei derivate, se va alege metoda corespunzatoare.
Implicit, o metoda este legata static (early, la compilare), iar daca se doreste o legare dinamica (late, la executie) se va declara virtuala prin scrierea cuvantului virtual inaintea metodei. O metoda virtuala a unei clase de baza, poate fi mostenita intr-o clasa derivata sau poate fi redefinita (inlocuita - overriding). O metoda se declara vituala in clasa de baza (nu se poate declara virtuala doar intr-o clasa derivata.
Se poate observa in exemplul urmator ca daca dorim ca rezultatul sa fie "Baza/Derivata" si la apelul functiei g, trebuie sa declaram functia f virtuala, altfel rezultatul ar fi "Baza/Baza
Metoda virtuala
# include <conio.h>
# include <iostream.h>
class Cb
};
class Cd : public Cb
};
void g(Cb* p)
void main ()
In exemplul urmator vom utiliza variabile de tip referinta la clasa de baza, respectiv la clasa derivata.
Metoda virtuala
# include <conio.h>
# include <iostream.h>
class Cb
};
class Cd : public Cb
};
void g(Cb& r)
void main ()
In urmatorul exemplu se poate deduce necesitatea legarii dinamice, pentru a putea calcula corect distanta de la un punct la un cerc si respectiv la un segment (la o dreapta). Din pacate pentru ca metoda Contine exista in dublu exemplar (Stanga si Dreapta) apelul nu se poate efectua cum ar fi de dorit!
Metoda virtuala
#include <conio.h>;
#include <iostream.h>;
#include <math.h>;
float Sqr (float x)
class Punct
Punct (Punct &P)
virtual float Dist (Punct P) ;
};
class Cerc : public Punct
float Dist (Punct P)
};
class Stanga : public Punct
Stanga (Punct& P) : Punct(P)
};
class Dreapta: public Punct
{ public:Dreapta(float x0, float y0) : Punct(x0,y0)
Dreapta(Punct& P) : Punct(P)
};
class Segm : public Stanga, Dreapta {
public: Segm(Punct St, Punct Dr) : Stanga(St), Dreapta(Dr)
float Dist (Punct C)
};
void main (void)
Aproape orice metoda poate fi virtuala, chiar si metodele inline, metodele friend (dar nu si functiile friend) operatorii (daca nu se redefinesc prin functii friend) si destructorii, in schimb constructorii si metodele statice nu pot fi virtuale.
In exemplul prezentat in continuare, al doilea produs ( ) nu va fi afisat daca operatorul de inmultire a doua numere rationale nu este declarat virtual, (deoarece operatorul va apela operatorul de inmultire pentru Q in loc de F cum ar trebui, a si b fiind din clasa F). Se observa ca la inmultirea a doua obiecte din clasa Q nu sunt tiparite fractiile care se inmultesc, pe cand la inmultirea a doua obiecte din clasa F fractiile care se inmultesc sunt tiparite.
Program Operator Virtual;
#include <conio.h>; #include <iostream.h>;
class Q
virtual Q operator * (Q& r)
Q& operator *= (Q& r)
void Tip (char* Ms)
};
class F : public Q {
public: F (int m=0, int n=1) : Q (m,n)
Q operator * (Q& r)
void main (void)
Rezultate:
p = 2/3
q = 4/5
p = 8/15
r = 8/15
a = 2/3
b = 4/5
d = 8/15
d = 8/15
In clasa de baza se poate declara o comportare generica avand un nume si o semnatura unica (urmand ca fiecare specializare sa ofere propria metoda specifica) prin:
a) metode nule care se declara astfel:
virtual Tip_met Nume_met (Lista_Par_Formali)
b) metode pure declarate in forma:
virtual Tip_met Nume_met (Lista_Par_Formali)
Ambele variante permit inlocuirea acestei metode cu o metoda proprie clasei specializate derivata din clasa de baza, permitand astfel solicitarea unui comportament general, dar propriu fiecarei specializari.
Metodele pure nu pot fi apelate, rolul lor fiind doar de a declara tipul, numele si parametrii unei metode abstracte care urmeaza sa fie redefinita concret in fiecare clasa specializata.
O clasa este abstracta daca ea contine cel putin o functie membru virtuala pura. O functie membru virtuala este pura daca ea este declarata (virtual antet ) dar nu este definita in clasa din care face parte ci intr-o clasa derivata. Deoarece clasele abstracte contin functii membru nedefinite (virtuale pure), nu se pot crea obiecte apartinand acestora (nu pot fi instantiate, dar se pot defini variabile de tip pointer sau referinta la o clasa abstracta), iar daca functiile virtuale nu sunt definite nici in clasele derivate, atunci si aceste clase devin abstracte (o clasa derivata dintr-o clasa abstracta ramane abstracta daca nu s-au redefinit toate metodele pure mostenite, iar in caz contrar devine clasa concreta).
O clasa abstracta realizeaza implementarea unei notiuni care nu poate fi concretizata (si atunci nu poate fi decat enuntata), dar surprinde o caracteristica comuna a claselor specializate din aceasta (care vor contine implementarile efective). Deci, o clasa abstracta va generaliza (abstractiza) comportamentul subclaselor specializate.
|
Intr-o ierarhie, clasa de baza poate avea niste proprietati care nu se pot defini decat in clasele derivate (anumite caracteristici depind de clasa derivata). In exemplul alaturat, cu toate ca se cunoaste greutatea unui animal, nu se poate spune ca este slab sau gras decat pentru o clasa derivata).
In exemplul urmator vom da trei caracterizari pentru un anumit animal in functie de greutatea lui si cea medie (slab/gras), varsta lui si cea medie (tanar/batran), si viteza lui (de deplasare) si cea medie (lent/rapid), acestea pentru un Porumbel, Urs sau Cal.
// Clase Abstracte- Metode Pure
#include <iostream.h>
#include <conio.h>
class Animal
virtual double Greut__Medie () ;
virtual double Varsta_Medie () ;
virtual double Viteza_Medie () ;
int Slab ()
int Tanar()
int Lent ()
void Tip ()
class Porumbel:public Animal {
public: Porumbel (double Kg, double Ani, double Km_H) :
Animal(Kg, Ani, Km_H)
double Greut__Medie ()
double Varsta_Medie ()
double Viteza_Medie ()
};
class Urs: public Animal {
public: Urs (double Kg, double Ani, double Km_H) :
Animal(Kg, Ani, Km_H)
double Greut__Medie ()
double Varsta_Medie ()
double Viteza_Medie ()
};
class Cal: public Animal {
public: Cal (double Kg, double Ani, double Km_H) :
Animal(Kg, Ani, Km_H)
double Greut__Medie ()
double Varsta_Medie ()
double Viteza_Medie ()
};
void main (void)
// Rezultate:
gras, tanar, lent
gras, batran, rapid
slab, tanar, rapid
|
In exemplul urmator vom apela o functie Draw pentru a desena o figura oarecare (Punct, Patrat sau Cerc) si o functie Arie care va aplica formula caracteristica fiecarei figuri geometrice:
// Clasa Abstracta- Metode Pure
# include <Conio.h>
# include <Process.h>
# include <Iostream.h>
# include <Graphics.h>
class Figura
virtual void Draw () = 0; // Pura sau Nula
virtual int Arie () = 0;
int Pret ()
};
class Punct : public Figura
void Draw()
int Arie()
};
class Patrat : public Figura
void Draw()
int Arie()
};
class Cerc : public Figura
void Draw()
int Arie()
};
void InitGraf(void)
void main (void)
cout << ' Pret = ' << Fig[i]->Pret << endl;
getche(); InitGraf();
for (i=0; i<n; i++)
getche(); closegraph();
|