250 likes | 338 Views
Template-uri de clase. Tratarea excepţiilor care pot apărea la alocarea dinamică a memoriei. Programarea calculatoarelor şi limbaje de programare II Capitolul 8. Obiective. Înţelegerea modului în care se pot folosi template-urile de clase pentru crearea grupurilor de tipuri de date
E N D
Template-uri de clase. Tratarea excepţiilor care pot apărea la alocarea dinamică a memoriei Programarea calculatoarelor şi limbaje de programare II Capitolul 8
Obiective • Înţelegerea modului în care se pot folosi template-urile de clase pentru crearea grupurilor de tipuri de date • Studierea modului în care se tratează excepţiile care pot apărea la alocarea dinamică a memoriei • Studierea modului în care se poate folosi auto_ptr pentru a preveni problemele de alocare şi dealocare a memoriei
Sumar • Template-uri de clase • Tratarea excepţiilor care pot apărea la alocarea dinamică a memoriei
Template-uri de clase • Template-uri (şabloanele) de clase • Tipuri parametrizate • se folosesc pentru crearea claselor generice • Pentru adaptarea unei astfel de clase, este nevoie de unul sau mai mulţi parametri de tip care transformă clasa dintr-una generică în una particulară
Template-uri de clase • Exemplu • Stiva (stack) • structură de date care permite stocarea unor valori • ordinea de citire a acestora este inversă ordinii în care au fost înscrise în colecţie • regula last-in-first-out specifică stivelor este valabilă indiferent de tipul elementelor care sunt încărcate • Vom prezenta o implementare generică a unei stive
Template-uri de clase template <class T> class Stack { public: Stack(int = 10); ~Stack() { delete[] stackPtr; } bool push(const T&); bool pop(T&); private: int size; int top; T* stackPtr; bool isEmpty() const {return top == -1;} bool isFull() const {return top == size-1;} }; T este parametrul de tip al clasei
Template-uri de clase template<class T> Stack<T>::Stack(int s) { size = s > 0 ? s : 10; top = -1; stackPtr = new T[size]; } Alocare dinamică generică pentru tipul de dată T
Template-uri de clase template<class T> bool Stack<T>::push(const T& pushValue) { if(!isFull()) { stackPtr[++top] = pushValue; return true; } return false; } Inserează elementul în stivă
Template-uri de clase template<class T> bool Stack<T>::pop(T& popValue) { if(!isEmpty()) { popValue = stackPtr[top--]; return true; } return false; } Şterge elementul din stivă
Template-uri de clase Instanţiază obiectul doubleStack de dimensiune 5 int main() { Stack<double> doubleStack(5); ... return 0; } Obiectul doubleStack este de tip Stack<double>
Template-uri de clase int main() { ... double f = 1.1; cout << "Inserarea elementelor in doubleStack\n"; while(doubleStack.push(f)) { cout << f << ' '; f += 1.1; } cout << "\nStiva este plina. " << "Nu se mai poate insera elementul " << f << "\n\nExtragerea elementelor din doubleStack\n"; ... } Inserarea elementelor in doubleStack 1.1 2.2 3.3 4.4 5.5 Stiva este plina. Nu se mai poate insera elementul 6.6
Template-uri de clase int main() { ... cout << "\n\nExtragerea elementelor din doubleStack\n"; while(doubleStack.pop(f)) cout << f << ' '; cout << "\nStiva este goala. " << "Nu se mai pot extrage elemente\n"; ... } Extragerea elementelor din doubleStack 5.5 4.4 3.3 2.2 1.1 Stiva este goala. Nu se mai pot extrage elemente
Sumar • Template-uri de clase • Tratarea excepţiilor care pot apărea la alocarea dinamică a memoriei
Tratarea excepţiilor care pot apărea la alocarea dinamică a memoriei • Vom vedea cum putem trata excepţiile care pot apărea în urma apelului constructorului unei clase atunci când se alocă dinamic memorie • Cum ar trebui, de exemplu, să se comporte constructorul clasei String atunci când apare o eroare la apelul operatorului new prin care se arată că nu este suficient spaţiu în memoria calculatorului pentru păstrarea reprezentării interne a şirului de caractere?
Constructorii, destructorii şi tratarea excepţiilor • Varianta 1: returnarea obiectului chiar dacă el nu a fost corect construit, în speranţa că utilizatorul clasei va face testele necesare înainte de a folosi obiectul • Varianta 2: setarea valorii unei variabile din afara constructorului prin a cărei valoare să semnalizăm eroarea care a apărut • Varianta 3: generarea unei excepţii prin care să semnalăm eroarea apărută în constructor
Procesarea excepţiilor care apar la apelul operatorului new • C++ standard • atunci cand apare o eroare la apelul lui new, acesta generează o excepţie de tip bad_alloc definită în fişierul header <new> • Compilatoare incompatibile cu C++ standard • operatorul new returnează valoarea 0 în caz de excepţie
Procesarea excepţiilor care apar la apelul operatorului new • Exemplu double *ptr[50]; try { for(int i = 0; i < 50; i++) { ptr[i] = new double[50000000]; cout << "S-au alocat 50000000 locatii double in ptr[" << i << "]\n"; } } catch(bad_alloc e) { cout << "A aparut urmatoarea exceptie: " << e.what() << endl; } S-au alocat 50000000 locatii double in ptr[0] S-au alocat 50000000 locatii double in ptr[1] S-au alocat 50000000 locatii double in ptr[2] A aparut urmatoarea exceptie: Allocation Failure
Procesarea excepţiilor care apar la apelul operatorului new • Este posibilă modificarea metodei standard prin care compilatorul tratează eroarea de nealocare a memoriei • set_new_handler • funcţie definită de C++ standard în header-ul <new> • primeşte ca argument implicit un pointer la o funcţie fără niciun argument şi care returnează void
Procesarea excepţiilor care apar la apelul operatorului new • Programatorul poate înregistra această funcţie ca fiind cea care se apelează la apariţia erorii la alocarea memoriei printr-un apel al lui set_new_handler • Odată înregistrată noua funcţie, operatorul new nu va mai genera excepţia bad_alloc, ci va executa corpul noii funcţii
Procesarea excepţiilor care apar la apelul operatorului new using std::set_new_handler; void customNewHandler() { cerr << "A fost apelata functia customNewHandler"; abort(); } int main() { ... set_new_handler(customNewHandler); ... } S-au alocat 50000000 locatii double in ptr[0] S-au alocat 50000000 locatii double in ptr[1] S-au alocat 50000000 locatii double in ptr[2] A fost apelata functia customNewHandler
Clasa auto_ptr şi alocarea dinamică a memoriei • Memory leak • O excepţie care apare după alocarea memoriei şi înainte de apelul operatorului delete pentru dealocarea memoriei poate conduce la imposibilitatea dealocării memoriei până la terminarea execuţiei aplicaţiei • Clasa template auto_ptr • declarată în header-ul <memory>
Clasa auto_ptr şi alocarea dinamică a memoriei • Un obiect din clasa auto_ptr menţine un pointer la zona de memorie alocată dinamic • Atunci când un obiect auto_ptr iese din domeniul său de definiţie (scope), se apelează automat operatorul delete pentru pointerul spre care pointează • Pot fi folosiţi operatorii *şi -> • obiectele tip auto_ptr pot fi folosite ca variabilele pointer obişnuite
Clasa auto_ptr şi alocarea dinamică a memoriei ... #include <memory> using std::auto_ptr; class Integer { public: Integer(int i = 0) : value(i) {cout << "Constructor pentru Integer: " << value << endl;} ~Integer() {cout << "Destructor pentru Integer: " << value << endl;} void setInteger(int i) {value = i;} int getInteger() {return value;} private: int value; }; ...
Clasa auto_ptr şi alocarea dinamică a memoriei ... int main() { ... auto_ptr<Integer> ptrToInteger(new Integer(7)); ... return 0; } Se creează obiectul ptrToInteger de tip auto_ptr<Integer> Obiectul ptrToInteger este iniţializat cu un pointer la un obiect de tip Integer alocat dinamic şi iniţializat cu valoarea 7
Clasa auto_ptr şi alocarea dinamică a memoriei ... int main() { ... ptrToInteger->setInteger(99); cout << "Intregul dupa setInteger: " << (*ptrToInteger).getInteger() << "\nTerminarea programului" << endl; return 0; } Folosirea operatorilor ->, * şi .