|
Politica de confidentialitate |
|
• domnisoara hus • legume • istoria unui galban • metanol • recapitulare • profitul • caract • comentariu liric • radiolocatia • praslea cel voinic si merele da aur | |
Clase derivate. Mosteniri C++ | ||||||
|
||||||
d7z3zi Se considera un program care descrie organizarea personalului unei institutii fara folosirea claselor derivate. O clasa numita Angajat detine date si functii referitoare la un angajat al institutiei: class AngajatA char * nume; float salariu; public: Diferite categorii de angajati necesita date suplimentare fata de cele definite in clasa Angajat, corespunzatoare postului pe care il detin. De exemplu, un sef de sectie (administator) este un angajat (deci sunt necesare toate datele care descriu aceasta calitate) dar mai sunt necesare si alte informatii, de exemplu precizare sectiei pe care o conduce. De aceea, clasa Administator trebuie sa includa un obiect de tipul Angajat , la care se adauga alte date: class AdministratorA Posibilitatea de a include intr-o clasa date descrise intr-o alta
clasa are in limbajele obiect-orientate un suport mai eficient, mai flexibil
si mai simplu de utilizat decat simpla includere a unui obiect din tipul
dorit: derivarea claselor. class Administrator : public AngajatA int sectie; public: void display(); Aceasta operatie care se poate reprezenta schematic prin indicarea unei derivari cu o sageata (arc directionat) de la clasa a derivata la clasa de baza. clasa de baza Angajat clasa derivata Administrator In general, derivarea unei clase se specifica in felul urmator: class nume_derivata : specificator_acces nume_baza A Specificatorul de acces poate fi unul din cuvintele-cheie: public, private,
protected. Cand o clasa este derivata dintr-o clasa de baza, clasa derivata
mosteneste toti membrii clasei de baza (cu exceptia unora: constructori, destructor
si functia operator de asignare). void Administrator::display()A cout << nume << “ ”<< salariu << endl; //
error cout << sectie << endl; In schimb, se poate folosi functia membra publica display() a clasei Angajat: void Administrator::display()A Redefinirea functiei display() in clasa derivata ascunde functia cu acelasi
nume din clasa de baza, de aceea este necesara calificarea functiei cu numele
clasei de baza folosind operatorul de rezolutie: Angajat::display(). class baza A public: int a, b; class derivata A public: int b, c; // b este redefinit Este posibila mostenirea din mai multe clase de baza. De exemplu: class X : public Y, public Z, ….public W A Evident, specificatorii de acces pot sa difere (pot fi oricare din public, private, protected). Mostenirea multipla va fi reluata intr-o subsectiune urmatoare. O clasa de baza se numeste baza directa, daca ea este mentionata in lista de clase de baza. O clasa de baza se numeste baza indirecta daca nu este baza directa dar este clasa de baza pentru una din clasele mentionate in lista claselor de baza. Intr-o clasa derivata se mostenesc atat membrii bazelor directe cat si membrii bazelor indirecte si, de asemenea, se pastreaza mecanismul de redefinire si ascundere a membrilor redefiniti. De exemplu: class A A public: void f();S; class B : public A A S; class C : public B Apublic: void f(); S; void f() A Prin mostenire multipla si indirecta se pot crea ierarhii de clase care se reprezinta prin grafuri aciclice directionate. 5.1.1 Constructori si destructori in clasele derivate Constructorii si destructorii sunt functii membre care nu se mostenesc. La
crearea unei instante a unei clase derivate (obiect) se apeleaza implicit mai
intai constructorii claselor de baza si apoi constructorul clasei
derivate. Ordinea in care sunt apelati constructorii claselor de baza
este cea din lista claselor de baza din declaratia clasei derivate. Constructorii
nu se pot redefini pentru ca ei, in mod obligatoriu, au nume diferite
(numele clasei respective). class BA Daca nici clasa de baza nici clasa derivata nu au definiti constructori, compilatorul
genereaza cate un constructor implicit pentru fieccare din clase si-i
apeleaza in ordinea baza, apoi derivata. ? Exemplul 5.1 class Angajat A char *nume; float salariu; public: S void Administrator::display() const A cout << "Sef sectie: " << sectie << endl; In functia fa1() sunt create doua obiecte, obiectul a1 din clasa Angajat
si obiectul m1 din clasa Administrator. Doua din argumentele de apel ale constructorului
obiectului m1 sunt transferate constructorului clasei de baza (argumentele const
char* n si float sal). Constructor baza Destructor derivata Aceste mesaje evidentiaza ordinea de apel a constructorilor si destructorilor clasei derivate, precum si selectia functiei display() redefinita in clasa derivata. Pentru cele doua clase din Exemplul 5.1 au fost definiti si constructorii de copiere corespunzatori. Se poate remarca ca argumentul constructorului de copiere al bazei este o referinta la un obiect derivat: Administrator::Administrator(Administrator& r) Acest lucru este posibil deoarece conversia de la o referinta la clasa derivata Administrator poate fi convertita fara ambiguitate in referinta la clasa de baza Angajat. Fie functia: void fa2()A Obiectul m2 este creat printr-un constructor de copiere: este apelat mai intai constructorul de copiere al clasei de baza, apoi constructorul de copiere al clasei derivate. Mesajele afisate la executia functiei fa2() sunt urmatoarele: Constructor baza Aceasta este situatia de executie corecta, deoarece au fost definiti corect
constructorii de copiere in clasa de baza si in clasa derivata. In ceea ce privesc destructorii, se poate observa si din exemplele date ca nu se mostenesc destructorii claselor de baza, dar la distrugerea unui obiect derivat este apelat mai intai destructorul clasei derivate si apoi destructorul clasei de baza. Acest lucru este valabil atat in cazul in care au fost definiti destructori in baza si derivata, cat si pentru destructorii impliciti generati de compilator. Dat fiind ca destructorii pot fi declarati functii virtuale, cateva aspecte relativ la comportarea destructorilor virtuali vor fi prezentate in subsectiunea 5.4 5.2 Controlul accesului la membrii clasei de bazaAccesul la membrii clasei de baza mosteniti in clasa derivata este controlat
de specificatorul de acces (public, protected, private) din declaratia clasei
derivate. 5.2.1 Mostenirea de tip public a clasei de baza Daca specificatorul de acces din declaratia unei clase derivate este public,
atunci: ? Exemplul 5.2 In acest exemplu sunt prezentate si comentate cateva din situatiile de acces la membrii clasei de baza din clasa derivata atunci cand specificatorul de acces este public. #include <iostream.h> class baza A int a; protected: int b; public: int c; void SetA(int x)Aa = x; cout << "SetA din baza\n";S void SetB(int y)Ab = y; cout << "SetB din baza\n";S void SetC(int z)Ac = z; cout << "SetC din baza\n";S S void SetC(int z) A c = z; cout << "SetC din derivata\n"; S S; void fb()A derivata D; D.a = 1; // eroare, a este private in baza D.SetA(2); // corect se apeleaza baza::SetA() D.b = 3; // eroare, b este protected D.baza::SetB(5); // corect, baza::SetB este public D.SetB(4); // corect, derivata::SetB este public D.c = 6; // corect, c este public D.baza::SetC(7); // corect, baza::SetC este public D.SetC(8); // corect, derivata::SetC este public S Daca se comenteaza liniile de program care provoaca erori si se executa functia fb(), se obtin urmatoarele mesaje la consola: SetA din baza 5.2.2 Mostenirea de tip protected a clasei de baza Daca specificatorul de acces din declaratia clasei derivate este protected,
atunci toti membrii de tip public si protected din clasa de baza devin membri
protected in clasa derivata. Bineinteles, membrii de tip private
in clasa de baza nu pot fi accesati din clasa derivata. class derivata:protected baza A Atunci, in functia fb() sunt identificate ca erori toate apelurile de functii ale clasei de baza pentru un obiect derivat, precum si accesul la variavila c din baza: void fb()A derivata D; SetB din derivata Reiese foarte pregnant, ca in mostenirea protected a unei clase de baza, nu mai pot fi accesati din afara clasei derivate nici unul dintre membrii clasei de baza. 5.2.3 Mostenirea de tip private a clasei de baza Daca specificatorul de acces din declaratia clasei derivate este private, atunci
toti membrii de tip public si protected din clasa de baza devin membri de tip
private in clasa derivata si pot fi accesati numai din functii membre
si friend ale clasei derivate. Din nou trebuie amintit ca membrii de tip private
in clasa de baza nu pot fi accesati din clasa derivata. class derivata : private baza A mesajele de erori de compilare si de executie ale functiei fb() sunt aceleasi
ca si in mostenirea protected. 5.2.4 Modificarea individuala a accesului la membrii clasei de baza Se pot modifica drepturile de acces la membrii mosteniti din clasa de baza prin declararea individuala a tipuli de acces. Aceste drepturi nu pot depasi, insa, caracterul private a membrilor clasei de baza. De exemplu, se reiau astfel cele doua clase din Exemplul 5.2: #include <iostream.h> class baza A int a; protected: int b; public: int c; Variabilele b si c din clasa baza au fost transformati in membri publici ai clasei derivata prin declaratiile: baza::b; baza::c; in zona de declaratii de tip public a clasei derivata. 5.3 Mostenirea multiplaAsa cum s-a aratat in subsectiunea 5.1, o clasa poate avea mai multe clase de baza directe, daca acestea sunt specificate in declaratia clasei. In exemplul urmator este prezentata clasa derivata care mosteneste doua clase de baza, baza1 si baza2. ? Exemplul 5.3 #include <iostream.h> class baza1A protected: int x; public: baza1(int i) A cout << "Constructor baza1\n"; x = i; In functia fbm() este creat un obiect D de clasa derivata. Constructorul clasei derivata transfera cate un argument constructorilor fiecarei clase de baza, care initializeaza datele membre x si y. La executia functiei fbm() se afiseaza urmatoarele mesaje, care indica ordinea in care sunt apelati constructorii si destructorii clasei derivate si a claselor de baza. Constructor baza 1 5.3.1 Clase de baza virtuale Intr-o mostenire multipla este posibil ca o clasa sa fie mostenita indirect de mai multe ori, prin intermediul unor clase care mostenesc, fiecare in parte, clasa de baza. De exemplu: class L A public: int x;S; class A : public L A /* */S; class B : public L A /* */S; class D : public A, public B A /* */S; Acesta mostenire se poate reprezenta printr-un graf aciclic directionat (directed acyclic graph -; DAG) care indica relatiile dintre subobiectele unui obiect din clasa D. Din graful de reprezentare a mostenirilor, se poate observa faptul ca L este replicata in clasa D. Un obiect din clasa D va contine membrii clasei L de doua ori, o data prin clasa A (A::L) si o data prin clasa B (B::L). Se pot reprezenta partile distincte ale unui astfel de obiect: In aceasta situatie, acesul la un membru al clasei L (de exemplu variabila x), al unui obiect de tip D este ambiguu si deci interzis (este semnalat ca eroare la compilare):D ob; ob.x = 2; // eroare D::x este ambiguu; poate fi in Se pot elimina amiguitatile si deci erorile de compilare prin calificarea variabilei cu domeniul clasei careia ii apartine: ob.A::x = 2; // corect, x din A ob.B::x = 3; // corect, x din B O alta solutie pentru eliminarea ambiguitatilor in mostenirile multiple este de impune crearea unei singure copii din clasa de baza in clasa derivata. Pentru aceasta este necesar ca acea clasa care ar putea produce copii multiple prin mostenire indirecta (clasa L, in exemplul de mai sus) sa fie declarata clasa de baza de tip virtual in clasele care o introduc in clasa cu mostenire multipa. De exemplu: class L A public: int x; S; class A : virtual public L A /* */ S; class B : virtual public L A /* */ S; class D : public A, public B A /* */S; O clasa de baza virtuala este mostenita o singura data si creaza o singura copie in clasa derivata. Graful de reprezentare a mostenirilor din aceste declaratii ilustreaza acest comportament: O clasa poate avea atat clase de baza virtuale cat si nevirtuale, chiar de acelasi tip. De exemplu, in declaratiile:class L A public: int x; S; class A : virtual public L A /* */ S; class B : virtual public L A /* */ S; class C : public L A /* */ S; class D : public A, public B, public C A /* */ S; clasa D mosteneste indirect clasa L: de doua ori ca o clasa de baza virtuala prin mostenirea din clasele A si B si inca o data direct, prin mostenirea clasei C. Reprezentarea grafica a unei astfel de mosteniri este urmatoarea: Un obiect din clasa D va contine doua copii a clase L: o singura copie prin mostenirea virtual prin intermediul claselor A si B si o alta copie prin mostenirea clasei C. Ambiguitatile care pot sa apara in astfel de situatii se rezolva prin calificarea membrilor cu numele clasei (domeniului) din care fac parte.5.4 Functii virtuale si polimorfism O functie virtuala este o functie care este declarata de tip virtual in
clasa de baza si redefinita intr-o clasa derivata. Redefinirea unei functii
virtuale intr-o clasa derivata domina (override) definitia functiei in
clasa de baza. Functia declarata virtual in clasa de baza actioneaza ca
o descriere generica prin care se defineste interfata comuna, iar functiile
redefinite in clasele derivate precizeaza actiunile specifice fiecarei
clase derivate. 5.4.1 Conversia pointerilor intre clase de baza si derivate Conversia unui pointer la o clasa derivata in pointer la o clasa de baza
a acesteia este implicita, daca derivarea este de tip public si nu exista ambiguitati.
Rezultatul conversiei este un pointer la subobiectul din clasa de baza al obiectului
din clasa derivata. Acest lucru este posibil deoarece un obiect dintr-o clasa
derivata contine cate un pointer ascuns la fiecare din subobiectele componente
(care sunt din obiecte din clasele de baza). ? Exemplul 5.4 #include <iostream.h> class B A /* */ S; class D:public B A /* */ S; void main()A B ob; S-a definit o clasa de baza B si o clasa derivata D. Conversia pointerului de tip D* (la un obiect din clasa derivata) in pointer de tip B* (la subobiectul component din clasa de baza) este legala si implicita. Conversia inversa nu este admisa implicit, iar fortarea prin operatorul de conversie cast este admisa de compilator dar produce rezultate nedeterminate la executie. ? Se mai poate adauga faptul ca si referintele pot fi convertite in mod
asemanator: o referinta la o clasa poate fi convertita implicit intr-o
referinta la o clasa de baza a acesteia daca derivarea este de tip public si
nu exista ambiguitati. Rezultatul conversiei este o referinta la subobiectul
component al obiectului de tip derivat. 5.4.2 Functii virtuale Atunci cand o functie normala (care nu este virtuala) este definita intr-o
clasa de baza si redefinita in clasele derivate, la apelul acesteia ca
functie membra a unui obiect pentru care se cunoaste un pointer, se selecteaza
functia dupa tipul pointerului, indiferent de tipul obiectului al carui pointer
se foloseste (obiect din clasa de baza sau obiect din clasa derivata). • Daca obiectul este din clasa de baza nu se poate folosi un pointer
la o clasa derivata (Exemplul 5.4). Acesta este mecanismul de virtualitate si el permite implementarea polimorfismului
in clasele derivate. O functie redefinita intr-o clasa derivata
domina functia virtuala corespunzatoare din clasa de baza si o inlocuieste
chiar daca tipul pointerului cu care este accesata este pointer la clasa de
baza. ? Exemplul 5.5 Se considera o clasa de baza B si doua clase derivate D1 si D2. In clasa
de baza sunt definite doua functii: functia normala f()si functia virtuala g().
In fiecare din clasele derivate se redefinesc cele doua functii f() si
g(). In functia main() se creaza trei obiecte: un obiect din clasa de
baza B indicat prin pointerul B* pb si doua obiecte din clasele derivate D1
si D2. Fiecare dintre obiectele derivate poate fi indicat printr-un pointer
la clasa derivata respectiva (D1* pd1, respectiv D2* pd2), precum si printr-un
pointer la baza corespunzator (B* pb1 = pd1, respectiv B* pb1 = pd1). #include <iostream.h> class B A public: void f() A cout << "f() din B\n"; S virtual void g()A cout << "g() din B\n"; S // Obiect B, pointer B* pb->f(); // f() din B pb->g(); // g() din B // Obiecte D1, D2 , pointeri D1*, D2* pd1->f(); // f() din D1 pd2->f(); // f() din D2 pd1->g(); // g() din D1 pd2->g(); // g() din D2 // Obiecte D1, D2 , pointeri B*, B* pb1->f(); // f() din B pb2->f(); // f() din B pb1->g(); // g() din D1 pb2->g(); // g() din D2 delete pb; delete pd1; delete pd2; Mesajele care se afiseaza la consola la executia functiei fv1() sunt cele inscrise
ca si comentarii in program. Polimorfismul, adica apelul unei functii dintr-o clasa derivata, este posibil numai prin utilizarea pointerilor la obiecte. Obietele insele determina univoc varianta functiei apelate, deci nu se pot selecta alte functii decat cele ale obiectului de tipul respectiv. De exemplu, pentru aceleasi clase definite ca mai sus, se considera functia fv2(): void fv2()A Comentariile arata care dintre functiile redefinite este utilizata, si anume functia care corespunde tipilui obiectului, indiferent daca este virtuala sau nu. In acest moment se pot preciza mai detaliat asemanarile si diferentele
dintre functiile supraincarcate (overloaded), functiile redefinite (redefined)
si functiile virtuale. In toate aceste situatii numele functiilor este
acelasi. Nu pot fi declarate functii virtuale: functii ne-membre, constructori, functii de tip static, functii friend. 5.4.3 Mostenirea functiilor virtuale Atributul virtual se mosteneste pe tot parcursul derivarii. Cu alte cuvinte,
daca o functie este declarata de tip virtual in clasa de baza, functia
redefinita intr-o clasa derivata este functie de tip virtual in
mod implicit (fara sa fie nevoie de declaratie explicita virtual). Acest lucru
inseamna ca, daca aceasta clasa derivata serveste pentru definirea unei
noi derivari, functia va fi mostenita de asemenea de tip virtual. class DD1:public D1A public: void f() A cout << "f() din DD1\n";S void g() A cout << "g() din DD1\n";S Mesajele care se obtin la executia functiei urmatoare sunt prezentate ca si comentarii: void fdd()A pb->g(); // g() din DD1 pd1->g(); // g() din DD1 pdd1->g(); // g() din DD1 S-a creat un obiect de tip clasa derivata DD1 in memoria heap folosind
operatorul de alocare dinamica new. Acest obiect poate fi indicat prin trei
pointeri: pointerul la clasa derivata DD1 (DD1* pdd1), returnat de operatorul
new, un pointer la clasa de baza B (B* pb) si un pointer la clasa de baza D1
(D1* pd1) . Ultimii doi pointeri au fost obtinut prin conversie implicita de
la primul pointer. Pot sa apara si alte situatii in mostenirea functiilor virtuale. De exemplu, daca o functie virtuala nu este redefinita intr-o clasa derivata, atunci se foloseste functia mostenita din clasa de baza si pentru obiecte de tip clasa derivata. Pentru precizarea acestei situatii se reiau clasele B, D1 si D2 din Exemplul 5.5, modificate astfel: #include <iostream.h> class B A public: virtual void g()A cout << "g() din B\n"; S void main() A Functia virtuala g()a fost redefinita in clasa D1 si apoi a fost apelata pentru un obiect de clasa D1. In clasa D2 functia virtuala g() nu a fost redefinita, si de aceea la invocarea pentru un obiect din clasa D2 este folosita functia g() din clasa de baza. 5.4.4 Destructori virtuali Destructorii pot fi declarati de tip virtual, si aceasta declaratie este necesara in situatia in care se dezaloca un obiect de tip clasa derivata folosind pointer la o clasa de baza a acesteia. De exemplu: #include <iostream.h> class BA public: Eroarea care apare la executia acestui program este aceea ca a fost creat un obiect de clasa D si a fost sters numai un subobiect al acestuia, subobiectul din clasa de baza B. Mesajele afisate evidentiaza acest lucru: Constructor B Acest lucru face ca memoria libera (heap) sa ramana ocupata in mod inutil (cu partea de date a clasei D), desi se intentiona eliminarea completa din memorie a obiectului creat. Declararea destructorului din clasa de baza de tip virtual rezolva aceasta problema: class BA public: Fara sa fie modificata clasa D sau functia main()de mai sus, la executia acesteia dupa declararea destructorului clasei B ca virtual, se obtin mesajele: Constructor B care indica faptul ca, la executia instructiunii delete p a fost apelat desctructorul clasei derivate D, dupa tipul obiectului (obiectul este de clasa D) nu al pointerului folosit (p este de tip B*). Destructorul D::ID() elibereaza partea din clasa derivata a obiectului, dupa care (in mod implicit, fara sa fie nevoie ca programatorul sa specifice acest lucru), este apelat destructorul clasei de baza B care elibereaza subobiectul de baza din obiectul dat. 5.4.5 Clase abstracte De cele mai multe ori functia declarata de tip virtual in clasa de baza
nu defineste o actiune semnificativa si este neaparat necesar ca ea sa fie redefinita
in fiecare din clasele derivate. Pentru ca programatorul sa fie obligat
sa redefineasca o functie virtuala in toate clasele derivate in
care este folosita aceasta functie, se declara functia respectiva virtuala pura.
O functie virtuala pura este o functie care nu are definitie in clasa
de baza, iar declaratia ei arata in felul urmator: virtual tip_returnat nume_functie(lista_arg) = 0; ? Exemplul 5.6 #include <iostream.h> class X A // clasa abstracta public: virtual void fp()=0;// functie virtuala pura void main ()A Din clasa abstracta X a fost derivata clasa, de asemenea abstracta, Y. Numai
clasa Z redefineste functia virtuala pura fp(), deci ea poate fi instantiata.
Un exemplu mai complet care evidentiaza utilitatea functiilor virtuale in general si a functiilor virtuale pure in special este Exemplu 5.7 de mai jos. ? Exemplul 5.7 #include <iostream.h> class ConvertA protected: double x; // valoare intrare double y; // valoare iesire public: Acest exemplu este un mic program de conversie a unor date dintr-o valoare
de intrare intr-o valoare de iesire; de exemplu, din grade Farenheit in
grade Celsius, din inch in centimetri, etc. 5.4.6 Polimorfismul Dupa cum s-a mentionat, una din caracteristicile importante ale programarii
orientate pe obiecte este aceea ca permite definirea unei interfete comune pentru
mai multe metode specifice diferitelor functionalitati. Aceasta comportare,
care asigura simplificarea si organizarea sistemelor de progrme complexe, este
cunosuta sub numele de polimorfism. Polimorfismul introdus prin mecanismul de
virtualitate este polimorfism la nivel de executie, care permite legarea tarzie
(late binding) intre evenimentele din program, in contrast cu legarea
timpurie (early binding), proprie apelurilor functiilor normale (nevirtuale).
Termenul de legare tarzie se refera la evenimente din timpul executiei. In astfel de apeluri, adresa functiei care urmeaza sa fie apelata nu este cunoscuta decat in momentul executiei programului. Functiile virtuale sunt evenimente cu legare tarzie: accesul la astfel de functii se face prin pointer la clasa de baza, iar apelarea efectiva a functiei este determinata in timpul executiei de tipul obiectului indicat, pentru care este cunoscut pointerul la clasa sa de baza. Avantajul principal al legarii tarzii (permisa de polimorfismul asigurat de functiile virtuale) il constitue simplitatea si flexibilitatea programelor rezultate. Bineinteles, exista si dezavantaje, cel mai important fiind timpul suplimentar necesar selectiei functiei apelate efectiv din timpul executiei, ceea ce conduce la un timp de executie mai mare al programelor. Cu alte cuvinte, folosirea functiilor virtuale trebuie rezervata numai pentru situatii in care sunt in mod real necesare, atunci cand beneficiul adus de polimorfism depaseste costul suplimentar de executie. 5.5 Implementarea colectiilor de date prin clase derivate Asa cum s-a prezentat in sectiunea 3, o colectie de date se caracterizeaza
prin modelul de date pe care il reprezinta, care da forma colectiei (de
exemplu, lista, arbore, multime) si prin tipul datelor (obiecte ale colectiei),
care pot fi de tip predefinit sau definit de utilizator. Definirea individuala
a claselor de reprezentare a unei colectii de o anumita forma pentru fiecare
tip de obiecte conduce la un numar imens de reprezentari, toate avand
aceeasi organizare si functionalitate si diferind doar prin tipul de date la
care se refera. S-a putut remarca acest lucru din definirea repetata a claselor
pentru reprezentarea tablourilor de diferite tipuri (tablouri de numere intregi,
tablouri de siruri de caractere, etc) sau a listelor inlantuite. Implementarea
mai generala a unor clase de colectii prin folosirea pointerilor generici ca
informatii in tablouri, liste sau arbori implica numeroase restrictii
in definirea si utilizarea acestora si in general sunt considerate
nesigure ca tip, datorita conversiilor explicite (cast) a tipurilor de pointeri,
si deci necontrolate de catre compilator. 5.1.2 Implementarea unui tablou de obiecte Pentru imnplementarea unui tablou de obiecte de diferite tipuri definite de utilizator se defineste clasa Object ca o clasa de baza pentru toate tipurile derivate: class ObjectA public: Clasa ObArray, derivata din clasa Object, defineste un vector de pointeri de tip Object* . Nu este necesar sa fie limitata dimensiunea vectorului deoarece se asigura cresterea dimensiunii acestuia atunci cand este necesar. class ObArray : public Object A Constructorul clasei ObArray construieste un vector de pointeri de dimensiune
(size) zero. int ObArray::Add(Object *x)A if(size<dimens) pasize++i=x; else A Functiile membre RemoveAt() si RemoveALL() elimina un pointer dintr-o pozitie data, fara sa stearga obiectul indicat de acesta, sau elimina intreg vectorul de pointeri, transformandu-l intr-un vector de dimensiune nula: int ObArray::RemoveAt(int i) A if(i<0 || i>=size) return 0; for(int j=i;j<size-1;j++) paji=paj+1i; size--; return 1; Celelate functii ale clasei sunt usor de inteles. Pentru o colectie de obiecte de o clasa data, se foloseste clasa Object ca si clasa de baza a acesteia. De exemplu, pentru reprezentarea unui vector de puncte in plan, clasa Point (definita in sectiunea 2) se modifica astfel incat sa aiba ca si clasa de baza clasa Object: class Point : public Object A double x,y; public: Pentru definirea unui vector de puncte, o prima modalitate este aceea de a crea o clasa noua, clasa PointArray derivata din clasa colectiei de baza, ObArray, in felul urmator: class PointArray : public ObArray A public: int PointArray::Add(Point *x)A return ObArray::Add((Object*)x);S deoarece, daca nu este redefinita functia Add() in clasa derivata PointArray, atunci oricum se foloseste functia Add() din clasa de baza ObArray, iar conversia unui pointer de tipul Point* in pointer la clasa de baza (Object*) este, de asemenea implicita. Exemplu de utilizare a clasei void fpa1()A int size = array.GetSize(); for(i=0;i<size;i++) A La executia acestei functii se afiseaza urmatoarele mesaje: X = 0 Y= 0 O a doua modalitate de a defini o colectie de forma vector pentru tipul de date Point, este aceea de a folosi direct clasa colectiei de baza, ObArray, cu specificarea de fiecare data a conersiei de tip necesare. De exemplu, daca se rescrie functia fpa1(), in care se declara : ObArray array; se obtine o eroare de compilare datorita faptului ca nu poate fi convertit implicit pointerul de tip Object* returnat de functia Add() a clasei ObArray in pointer de tipul Point*. Solutia este simpla, de a defini o conversie explicita astfel: void fpa1()A int size = array.GetSize(); for(i=0;i<size;i++) A La executia acestei functii se obtin aceleasi rezultate ca si la executia functiei
precedente. Implementarile colectiilor de date prezentate in aceasta sectiune, ca
si in sectiunea 3, folosesc clase concrete, in sensul ca toate operatiile
acestora sunt definite si ele pot fi folosite pentru instantieri. Clasele concrete
(tipurile concrete -; concrete data types) permit definirea fiecarui concept
care nu are suport predefinit in limbaj, dar nu exprima partile comune
ale mai multor implementari ale unui anumit concept (model de date). De exemplu,
o multime poate fi implementata printr-o lista sau printr-un vector, dar clasele
concrete care reprezinta aceste structuri nu pot exprima conceptul de multime
comun celor doua modalitati de implementare. class Set A public: virtual void insert(T*) = 0; virtual void remove(T*) = 0; virtual int lookup(T*) = 0; Absenta constructorului si prezenta destructorului virtual este tipica in clasele abstracte. Toate functiile membre (cu exceptia destructorului) sunt functii virtuale pure. Fie clasele TList, respectiv TArray, clasele care definesc o lista, respectiv un vector de obiecte de tip T. Folosind aceste clase se pot defini mai multe implementari ale unei multimi. De exemplu: class TArraySet : public Set, private TArray A int index; public: void insert(T*); void remove(T*); int lookup(T*); TArraySet(int size): void fproc(Set* s) A for (T* p = s.first(); p = s.next())A Asadar, clasele container folosite pentru implementarea colectiilor de date
sunt clase abstracte care asigura definirea unui concept intr-un mod care
permite coexistenta intr-un program a mai multor implementari ale acestuia.
|
||||||
|
||||||
|
||||||
Copyright© 2005 - 2024 | Trimite document | Harta site | Adauga in favorite |
|