Laboratorul 09: Functii si Clase Template

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++:

  • Capitolul 16 (Chapter 16: Templates, pag. 694-729)

1. Functii Template

Template = Sablon

Functiile template sunt utilizate pentru a substitui implementari multiple pentru o functie atunci cand singura diferenta dintre acestea apare numai prin faptul ca folosim alte tipuri de date. Semnatura functiei are acelasi numar de parametri, insa tipurile de date difera.

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.

Functia este parametrizata dupa tipurile de date generice, nu specializata pe un singur tip de date.

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:

Max.cpp
#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.

MaxTemplate.cpp
#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;
}

Tipul unui parametru template este determinat in momentul compilarii.

La apelul unei functii template legarea se face in mod static.

NU o sa putem avea functii virtuale template.

In afara de tipuri generice, functiile template pot fi parametrizate si dupa parametri obisnuiti, similari cu cei folositi in functii.

//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;
}

Putem specifica un tip default pentru un tip generic, iar daca acesta nu este specificat la apelul functiei, se va folosi tipul default.

//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

Nu toate versiunile de compilatoarele permit argumente (tipul sau valoarea) template default pentru functii (permis de la C++11 in sus).

2. Clase Template

Exista posibilitatea realizarii de clase template – cu atribute template si/sau functii membre care folosesc atribute de tipuri generice.

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
};

Ca si in cazul functiilor template, se pot transmite si parametri care nu sunt tipuri de date intre <> sau tipuri template default.

Exemplu:

TemplateClass.cpp
#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;
}

Functiile template sunt un exemplu de polimorfism la compilare (compile time polymorphism) ca si supradefinirea functiilor.

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.

3. Derivarea din/de Clase Template

Modele de derivare simpla:

  • O clasa obisnuita derivata dintr-o clasa template
  • O clasa template derivata dintr-o clasa obisnuita de baza
  • O clasa template derivata dintr-o alta clasa template

O clasa template este un sablon pentru o clasa, ea va fi instantiata in functie de tipurile de date pe care le foloseste.

O instantiere conduce la specializarea acelei clase.

Procesul se numeste instantiere, iar rezulatul se numeste specializare.

3.1. Clasa obisnuita derivata din specializarea unei clase template

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 o sa mosteneasca atributul a de tip int din specializarea clasei template de baza.

Clasa derivata NU o sa fie clasa template.

3.2. Clasa obisnuita derivata dintr-o clasa template nespecializata

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; }
        ...
};

Clasa derivata o sa mosteneasca atributul a de tip generic T din clasa template de baza.

La randul ei, clasa derivata o sa fie tot o clasa template.

3.3. Clasa template derivata dintr-o clasa de baza neparametrizata

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 mosteneasca atributul a de tip int din clasa de baza, in mod firesc.

Clasa derivata o sa fie o clasa template, deoarece aceasta are atributul b de tip generic T.

3.4. Clasa template derivata dintr-o alta clasa template

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; }
        ...
};

Clasa derivata o sa mosteneasca atributul a de tip generic T din clasa template de baza.

La randul ei, clasa derivata o sa fie tot o clasa template, avand inca un atribut b de tip generic X.

Cand derivam trebuie sa ne punem problema ce urmeaza sa mostenim in clasa derivata:

  • o clasa template: ceva_ce_foloseste<T>
  • specializarea clasei template: ceva_ce_foloseste<int>

poo-is/laboratoare/09.txt · Last modified: 2020/10/03 11:31 by alexandru.ionita99
CC Attribution-Share Alike 3.0 Unported
www.chimeric.de Valid CSS Driven by DokuWiki do yourself a favour and use a real browser - get firefox!! Recent changes RSS feed Valid XHTML 1.0