In cadrul acestui laborator vom aprofunda concepte de programare generica, mai exact functiile si clasele template. Acestea ne vor permite sa reutilizam cod deja scris in contexte cat mai variate.
Ca referinte externe, recomandam urmatorul capitol din Absolute C++:
Spre exemplu, intr-o aplicatie exista posibilitatea de a avea nevoie de o functie de sortare pentru diferite tipuri de date (int, double, char). In loc sa scriem cate o functie sort() pentru fiecare tip de date, putem scrie o singura functie template generica care sa primeasca tipul de date ca parametru.
Functiile template folosesc cuvantul template pentru a preciza compilatorului faptul ca este vorba de o functie speciala, iar intre <> se precizeaza care sunt tipurile generice folosite de functie: <Continut>.
Tipul generic T este specificat prin: typename T sau class T.
Un parametru template este un tip special de parametru care poate sa fie folosit pentru a transmite un tip generic ca argument intr-o functie.
Functiile template se declara astfel:
template <class tip1, ... , class tipN> tip_de_date_returnat nume_functie(semnatura) { ... };
sau
template <typename tip1, ... , typename tipN> tip_de_date_returnat nume_functie(semnatura) { ... };
Exemplu:
#include <iostream> using namespace std; //Functie pentru maximul dintre doua numere de tip int int myMax(int x, int y) { return (x > y) ? x : y; } //Functie pentru maximul dintre doua numere de tip double double myMax(double x, double y) { return (x > y) ? x : y; } //Functie pentru maximul dintre doua date de tip char char myMax(char x, char y) { return (x > y) ? x : y; }
Observam ca cele trei functii difera doar prin tipul de date returnat si prin tipurile de date ale parametrilor, astfel putem folosi o singura functie tamplate pentru generalizare si evitam scrierea a trei functii diferite.
#include <iostream> using namespace std; //Functie pentru maximul dintre doua date de tipul generalizat T template <typename T> T myMax(T x, T y) { return (x > y) ? x : y; } int main() { cout << myMax<int>(21, 10) << endl; //apelam functia pentru tipul de date int cout << myMax<double>(0.07, 31.56) << endl; //apelam functia pentru tipul de date double cout << myMax<char>('a', 'x') << endl; //apelam functia pentru tipul de date char return 0; }
La apelul unei functii template legarea se face in mod static.
NU o sa putem avea functii virtuale template.
//Functie template cu parametru generic si parametru de tip int template <typename T, int dim> T sum(T *vect) { T s(0); for (int i = 0; i < dim; i++) { s += vect[i]; } return s; } //Utilizare int main() { int n = 10; int *vect1 = new int[n]; double *vect2 = new double[n]; for (int i = 0; i < n; i++) { vect1[i] = i; vect2[i] = (double) i / 2; } cout << sum<int, 10>(vect1) << endl; //Functia este specializata pentru int, iar dim = 10 cout << sum<double, 10>(vect2) << endl; //Functia este specializata pentru double, iar dim = 10 return 0; }
//Parametrul generic este setat default int, iar parametrul int este setat default la valoarea 10 template <typename T = int, int dim = 10> T sum(T *vect) { T s(0); for (int i = 0; i < dim; i++) { s += vect[i]; } return s; }
Acum, pentru vectorul nostru vect1 cu 10 elemente de tip int putem apela astfel:
cout << sum<>(vect1) << endl; //Nu specificam niciun tip si vor fi folosite cele default
Sintaxa pentru clasele template:
template <class tip1, ... , class tipN> class nume_clasa { tip1 a; tip2 b; ... tipN *vect; //Alte atribute si/sau metode };
sau
template <typename tip1, ... , typename tipN> class nume_clasa { tip1 a; tip2 b; ... tipN *vect; //Alte atribute si/sau metode };
Exemplu:
#include <iostream> using namespace std; //Clasa template template <class T> class myPair { private: T a, b; //Atribute de tip T public: myPair(T first, T second) { a = first; b = second; } //Constructor cu parametrii T getMax(); //Functie template }; template <class T> T myPair<T>::getMax() { return (a > b) ? a : b; } int main () { myPair<int> Obj(100, 75); cout << Obj.getMax() << endl; //Se va afisa 100 return 0; }
Fiecare apel specializat al unei functii/clase template conduce la compilare la generarea cate unei instante pentru fiecare specializare.
Daca o functie/clasa template contine o variabila locala statica/atribut static – fiecare instantiere a clasei template va contine copia proprie a variabilei/atributului static.
Modele de derivare simpla:
O instantiere conduce la specializarea acelei clase.
Procesul se numeste instantiere, iar rezulatul se numeste specializare.
template <typename T> class Baza { protected: T a; public: Baza(T i) { a = i; } ... }; class Derivata:public Baza<int> { private: int b; public: Derivata(int i, int j):Baza(i) { b = j; } ... };
Clasa derivata NU o sa fie clasa template.
template <typename T> class Baza { protected: T a; public: Baza(T i) { a = i; } ... }; template <typename T> class Derivata:public Baza<T> { private: int b; public: Derivata(T i, int j):Baza<T>(i) { b = j; } ... };
La randul ei, clasa derivata o sa fie tot o clasa template.
class Baza { protected: int a; public: Baza(int i) { a = i; } ... }; template <typename T> class Derivata:public Baza { private: T b; public: Derivata(int i, T j):Baza(i) { b = j; } ... };
Clasa derivata o sa fie o clasa template, deoarece aceasta are atributul b de tip generic T.
template <typename T> class Baza { protected: T a; public: Baza(T i) { a = i; } ... }; template <typename X, typename T> class Derivata:public Baza<T> { private: X b; public: Derivata(T i, X j):Baza<T>(i) { b = j; } ... };
La randul ei, clasa derivata o sa fie tot o clasa template, avand inca un atribut b de tip generic X.