|
Politica de confidentialitate |
|
• domnisoara hus • legume • istoria unui galban • metanol • recapitulare • profitul • caract • comentariu liric • radiolocatia • praslea cel voinic si merele da aur | |
Clase si functii template in C++ | ||||||
|
||||||
Template-urile (sabloanele) permit crearea de clase sau functii generice, in
care tipurile de date asupra carora se opereaza sunt specificate ca argumente.
Un template defineste o familie de tipuri (clase) sau functii. s7c12cp template<class X, class Y, int s> declaratie 7.1 Clase templateO clasa template specifica modul in care pot fi construite clase individuale, diferentiate prin tipul sau tipurile de date asupra carore se opereaza. O clasa template pentru definirea unei stive de obiecte de un tip oarecare T poate arata in felul urmator: template<class T> class TStackA T *bot; T *top; int size; public: TStack(int
s)A size = s; bot = top = new Tasi; S ITStack()Adelete ai bot;S void push(T
t)A*top++ = t;S T pop()Areturn *--top;S Clasa template TStack contine ca date private doi pointeri: pointerul bot,
pentru memorarea adresei de inceput a stivei si pointerul top, pentru
memorarea capului stivei. Dimensiunea size a tabloului unidimensional (vector),
creat dinamic la constructia unui obiect stiva, este necesara pentru testarea
depasirii capacitatii stivei in functia push(), dar aceste teste nu au
mai fost prezentate, pentru simplicitate. class IStackA int *bot; int *top; int size; public: IStack(int s)A size = s; bot = top = new intasi; Utilizarea template-urilor nu implica mecanisme run-time ci doar generarea de catre compilator a fiecarei clase care corespunde tipului (sau tipurilor) de date folosit la declararea unui obiect. ? Exemplul 7.1 Se considera implementarea unui program (programul Template) care contine descrierea clasei template TStack precum si a claselor Complex si String, care se pot prelua din sectiunile precedente. Fie functia: void ft1()A TStack<String> sstack(100); sstack.push("Primul sir\n"); sstack.push("Al doilea sir\n"); sstack.push("Al treilea sir\n"); cout << sstack.pop(); cout << sstack.pop(); cout << sstack.pop(); TStack<Complex> cstack(100); Declararea variabilelor istack, dstack, cstack si sstack impune crearea claselor corespunzatoare tipurilor integer, double, Complex si, respectiv String. In fiecare stiva se introduc, se extrag si se afiseaza la consola date de tipul respectiv. Rezultatul executiei functiei ft1() este urmatorul: 7 5 O observatie importanta in legatura cu operatiile definite intr-o
clasa template este aceea ca o clasa care defineste un tip de date transmis
ca argument al clasei template trebuie sa aiba prevazute functii de supraincarcare
a operatorilor utilizati in clasa template. template<class T> T TStack<T>::pop()A return *--top; Toate functiile membre ale unei clase template sunt functii template, adica functii care definesc un set general de operatii care vor fi aplicate unor tipuri de date diferite. In afara de functiile template membre ale unei clase, se pot defini si functii template nemembru, care definesc familii de functii in acelasi mod in care clasele template definesc familii de clase. Un astfel de exemplu este ilustrat printr-o functie template de sortare sort(), pentru care s-a folosit cel mai simplu algoritm, prin insertie lineara, deoarece in acest moment interesul este orientat catre tehnica de functie template si trasaturile limbajului, si mai putin catre agoritmul propriuzis. template<class T> void sort(T* vect, int n)A int i,j; Pentru fiecare apel, tipul argumentului determina functia de sortare care va fi folosita. ? Exemplul 7.2 Se introduce functia template sort() in programul Template si fie functia ft2()definita astfel: void ft2()A double dvectai = A4.7, 0.66, 7.0, 1,8, 3.0S; int size = sizeof(dvect)/sizeof(double); sort(dvect, size); for (int i=0; i<size;
i++) cout << dvectaii << " "; int ivectai = A10, 9, 5, 3, -2, 4S; size = sizeof(ivect)/sizeof(int); sort(ivect, size); for (i=0; i<size; i++)
cout << dvectaii << " "; Compilatorul creaza cate o functie de sortare pentru fiecare tip de data folosit ca argument de apel, deci se creaza o functie de sortare pentru vectori de numere intregi si, respectiv pentru vectori de numere de tip double. Dupa apelul functiei sort() datele sortate sunt afisate la consola. Rezultatul executiei acestei functii este urmatorul: 0.66 1.8 3.0 4.7 7.0 Cea mai simpla modalitate de a crea colectii sigure ca tip (type-safe) care
sa contina obiecte de orice tip este prin utilizarea claselor template. Clasa
sau clasele care descriu forma colectiei (vector, lista, etc.) se definesc ca
si clase template, avand ca argument tipul de date din care se vor crea
colectiile respective. La declararea unei colectii pentru un tip dat ca argument,
compilatorul creaza colectia de forma data de clasa template si pentru tipul
de date primit ca argument. Nu se folosesc conversii explicite de pointeri,
astfel incat aceste clase de colectii sunt sigure ca tip. 7.3.1 Implementarea unui vector asociativ Un vector asociativ (associative array, dictionary, map) este o colectie de
elemente, fiecare element fiind compus din doua parti: o parte numita cheie
(key), care este utilizata pentru a accesa celalalta parte a elementului, numita
valoare (value). template <class K, class V> class MapNodeA friend class Map<K,V>; friend class MapIter<K,V>; Declararea celor doua clase friend Map si friend MapIter asigura crearea si
manevrarea listei inlantuite de elemente. template<class K, class V> class MapA friend class MapNode<K,V>; friend class MapIter<K,V>; S; Valorile implicite ale cheii (key) si ale valorii (value) sunt memorate in
datele membre def_k si def_v. La crearea unui obiect din clasa Map, aceste valori
implicite se transmit ca argumente ale constructorului. O data membra din clasa
Map, pointerul current la un obiect de tipul MapNode, memoreaza pozitia curenta
in lista inlantuita, adica ultima pozitie accesata prin functia
operator de indexare. template<class K, class V> A if(head == 0)A current = head = new MapNode<K, V>(k, def_v); current->left = current->right = 0; return current->value; S MapNode<K,V>* p = head; for (;;)A if(p->key == k) A // cheie gasita current = p; return current->value; S if (k < p->key)A // inserare inainte de p current = new MapNode<K, V>(k, def_v); current->left = p->left; current->right = p; if (p == head) head = current; else p->left->right = current; p->left = current; return current->value; Functia operatorai() se comporta in felul urmator: daca se gaseste un element a carui cheie este identica cu argumentul functiei operator ai () (considerat ca un indice generalizat), atunci se returneaza referinta la valoarea (value) corespunzatoare acestei chei. Daca, parcurgand lista inlantuita incepand cu primul element (head), la un moment dat, cheia argument este mai mica decat cheia elementului curent, se deduce ca nu exista un element cu aceasta cheie si atunci se creaza un element cu valoare (value) implicita, care se insereaza in lista ordonata. La fel, daca lista a fost parcursa in intregime si cheia de cautare este mai mare decat cheia ultimului element, se creaza un element nou cu valoare (value) implicita care se insereaza la sfarsitul listei. O tratare separata necesita cazul unei liste vide (head = 0). In aceasta situatie se creaza de asemenea un element nou, cu valoare (value) implicita care devine primul element al listei. ? Exemplul 7.3 Se implementeaza un program (programul MapIter) in care se defineste un vector asociativ generic folosind clasele MapNode si Map prezentate mai sus. In functia fm1() se creaza un vector asociativ smap ca un obiect din clasa template Map cu tipul de date intreg (int) si valoare implicita -;1 pentru argumentul key si tipul definit mai sus String, cu valoare implicita “Default” pentru argumentul value. Folosind operatorul de indexare se introduc elemente in vector, se extrag si se afiseaza la consola. void fm1()A La executia functiei fm1(), se obtin urmatoarele mesaje la consola: String 3 De cele mai multe ori, pentru parcurgerea intr-o ordine dorita a elementelor
unei colectii de obiecte, asa cum este un vector asociativ, se foloseste un
mecanism numit iterator. Un iterator permite parcurgerea in ordinea dorita
a elementelor, fara ca aceasta ordine sa depinda de modul de ordonare interna
a elementelor colectiei. template <class K, class V> class MapIter Clasa MapIter se defineste pentru parcurgerea unei liste inlantuite de
tipul Map compusa din noduri de tipul MapNode si de aceea contine doi pointeri
map si node la aceste tipuri de obiecte. Un obiect din clasa MapIter se poate
construi implicit, cu pointerii map si node setati la valoarea 0, sau printr-unul
din cei doi constructori de initializare, care seteaza corespunzator cei doi
pointeri, in functie de argumentele primite. Pointerul node indica elementul
curent din lista. Functiile membre key() si value() ale clasei MapIter returneaza cheia, respectiv valoarea elementului curent din lista, indicat de pointerul node. ? Exemplul 7.4 Un vector asociativ (obiect din clasa Map) poate fi parcurs iterativ folosind un obiect din clasa MapIter (numit si iterator), care acceseaza succesiv elementele listei inlantuite. In programul MapIter se adauga functia fm2() in care este creat un vector asociativ cu numele table, cu cheie de tip String si valoare de tip intreg. Cheia si valoarea elementelor listei se afiseaza la consola la parcurgerea iterativa a listei folosind iteratorul iter. Prin apelul operatorului supraincarcat de incrementare al clasei MapIter, elementele listei sunt parcurse in ordine crescatoare a cheii. void fm2()A tablea"word1"i = 5; tablea"word3"i = 2; tablea"word3"i +=4; tablea"word2"i = 8; MapIter<String,int> iter(table); for (; iter; ++iter) cout << iter.key() << iter.value() << endl; La executia acestei functii sunt afisate la consola urmatoarele mesaje: word1 5 In operatia de testare a limitei buclei for se subintelege testul
(iter != 0) pentru efectuarea caruia are loc o operatie de conversie de la tipul
MapIter la pointer la void folosind functia operator de conversie operator void*()a
clasei MapIter. Acesta functie returneaza zero daca iteratorul nu se refera
la nici un element, iar altfel o valoare diferita de zero. Pentru acelasi vector asociativ (obiect din clasa Map) se pot crea mai multe obiecte iterator (din clasa MapIter), fiecare dintre ele parcurgand lista intr-un mod diferit, in functie de necesitatile programului. Fiecare iterator memoreaza un anumit nod curent din lista (in variabila node), in timp ce in lista insasi, nodul curent (variabila current) memoreaza ultimul nod accesat de oricare dintre iteratorii care o parcurg. In utilizarea din functia fm2(), tipul argumentului cheie este clasa String. Dat fiind ca in functia operator ai() a clasei Map se executa operatii de comparatie asupra cheii, este necesar ca in clasa String sa fie supraincarcati acesti operatori si acest lucru apare in implementarea clasei String. ? Exemplul 7.5 Se modifica functia fm2() astfel incat vectorul asociativ table sa fie inspectat simultan prin intermediul a doi iteratori, iter1 si iter2, din care iteratorul iter1 parcurge lista din element in element in ordinea crescatoare a cheii, iar iteratorul iter2 parcurge lista din doua in doua elemente in ordinea crescatoare a cheii. void fm3()A tablea"word1"i = 5; tablea"word3"i = 2; tablea"word3"i +=4; tablea"word2"i = 8; tablea"word4"i = 9; tablea"word5"i = 7; MapIter<String,int> iter1(table); for (; iter2; iter1++, iter2+=2) A cout << iter1.key() << iter2.value() << endl; cout << iter2.key() << iter2.value() << endl; La executia functiei fm3() se afiseaza la consola urmatoarele mesaje: word1 5 Pentru parcurgerea din doua in doua elemente a listei inlantuite, in ordinea crescatoare a cheii, trebuie sa fie supraincarcata functia operator+=() a clasei MapIter astfel: template <class K, class V> |
||||||
|
||||||
|
||||||
Copyright© 2005 - 2024 | Trimite document | Harta site | Adauga in favorite |
|