You are on page 1of 36

Programare orientata pe obiecte

Curs 10 – Template

C
++{};
C++ Acest curs
› std::type_info
› auto
› Functii template
› Clase template
C++ std::type_info
› class type_info;
› Retine informatii despre un tip de date
› Clasa nu poate fi instantiata
› Member functions:
operator== Compare types(public member function)
operator!= Compare types(public member function)
before Compare order of types(public member function)
name Get type name(public member function)
hash_code Get type hash code(public member function)
C++ std::type_info
int x = 10;
cout << "typeid(x): " << typeid(x).name() << endl;

const std::type_info& r = typeid(int);


cout << "typeid(int): " << r.name() << endl;

typeid(x): i
typeid(int): i
C++ std::type_info
class Base {}; // non-polymorphic
class Derived : Base {};

class Base2 { public: virtual void foo() {} }; // polymorphic


class Derived2 : Base2 {};

Base y;
cout << "typeid(Base):" << typeid(y).name() << endl;

Base2 z;
cout << "typeid(Base2):" << typeid(z).name() << endl;

Derived2 d;
cout << "typeid(Derived2):" << typeid(d).name() << “ and hash_code(): " <<
typeid(d).hash_code() << endl;
typeid(Base):Base
typeid(Base2):Base2
typeid(Derived2):Derived2 and hash_code(): 17576452764993780389
C++ auto
› Deducerea automata a tipului de date a unei expresii intr-un
limbaj de programare (disponibil cu C++11).
int main()
{
auto x = 4;
auto y = 3.37;
auto ptr = &x; i
cout << typeid(x).name() << endl D
<< typeid(y).name() << endl Pi
<< typeid(ptr).name() << endl;

return 0;
}
C++ auto
› auto devine in tip de date chiar daca o functie intoarce o
referinta catre acel tip de date
// functie ce returneaza o referinta catre tipul int
int& fun() { … }

// m va fi de tipul int in loc de int&


auto m = fun();

// n va fi de tipul int& datorita utilizarii &


auto& n = fun();
C++ Necesitatea parametrizării tipului de date
int main()
{
char vs[] = { 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k' };
int vi[] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
double vf[] = { 0.0, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0, 10.0 };
f(vs, 10);
f(vi, 10);
f(vf, 10);
return 0;
}
void f(int *v, int n) void f(double *v, int
void f(char *v, int n)
{ {
{
for (int i = 0; i < n; ++i) for (int i = 0; i <
for (int i = 0; i < n; ++i)
{ {
{
cout << v[i] << ", "; cout << v[i] <<
cout << v[i] << ", ";
} }
}
cout << endl; cout << endl;
cout << endl;
} }
}
C++ Necesitatea parametrizării tipului de date
void f(auto *v, int n)
{
for (int i = 0; i < n; ++i)
{
cout << v[i] << ", ";
}
cout << endl;
}
int main()
{
char vs[] = { 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k' };
int vi[] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
double vf[] = { 0.0, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0, 10.0 };
f(vs, 10);
f(vi, 10);
f(vf, 10);
return 0;
}
C++ Necesitatea parametrizării tipului de date

a, b, c, d, e, f, g, h, i, j,
0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
0, 1, 2, 3, 4, 5, 6, 7, 8, 9,

jdoodle.cpp:23:8: warning: use of 'auto' in parameter declaration only available


with -fconcepts
void f(auto *v, int n)
C++ Necesitatea parametrizării tipului de date
› Fie funcţia ce realizează suma elementelor unui vector de elemente de tip
double

double Sum0(double* a, int n)


{
double res = 0;

for(int i = 0; i < n; ++i)


{
res += a[i];
}
return res;
}
C++ Necesitatea parametrizării tipului de date
› Fie un vector cu elemente de tip float. Poate fi utilizază funcţia Sum0 pentru suma
elementelor?
double a[10];
float b[10];
// ...
double sa = sum0(a, 10); // OK
float sb = sum0(b, 10); // eroare de compilare
› Soluţie clasică:
float Sum0(float* a, int n)
{
float res = 0;

for(int i = 0; i < n; ++i)


{
res += a[i];
}
return res;
}
› Cum se poate generaliza această funcţie ?
C++ Necesitatea parametrizării tipului de date
› Pentru majoritatea tipurilor de date utilizate ar trebui definită câte o funcţie
› Spaţiul ocupat de program creşte

› Posibilitatea de a trimite tipul de date ca parametru ar permite existenţa unei


singure funcţii pentru suma elementelor unui vector
template<typename T>
T sum1(T* a, int n)
{
T res = 0;
for(int i = 0; i < n; ++i)
{
res += a[i];
}
return res;
}
C++ Necesitatea parametrizării tipului de date

double a[10];
float b[10];
int c[10];
// ...
double sa = sum1(a, 10); // OK
float sb = sum1(b, 10); // OK
int sc = sum1(c, 10); // OK
C++ Introducere
› Template-urile (şabloane) furnizează suport pentru programare generică
utilizând tipurile de date ca parametri
› Mecanismul template al limbajului C++ permite ca un tip de date sau o
valoare să fie un parametru în definirea unei clase, funcţii sau alias.
› Template-urile furnizează o cale directă de a reprezenta o gamă largă de
concepte generale şi căi simple de a le combina.
› Rezultatul din punct de vedere al claselor şi funcţiilor poate fi un cod mai
puţin general la run-time şi eficient din punct de vedere al spaţiului.
› Template-urile au fost introduse având ca scop proiectarea, implementarea
şi utilizarea librăriei standard.
› Librăria standard cere un mare grad de generalitate, flexibilitate şi eficienţă.
C++ Introducere
› În consecinţă, tehnicile care pot fi utilizate în proiectarea şi implementarea
libăriei standard sunt eficace şi eficiente în proiectarea soluţiilor pentru o
mare varietate de probleme.
› Aceste tehnici permit unui programator să ascundă implementări sofisticate
în spatele unei simple interfeţe şi să expună complexitatea utilizatorului
atunci când acesta are nevoie şi cere acest lucru.
C++ Un simplu template string
› Se consideră un şir de caractere definit ca o clasă ce memorează caractere
şi furnizează operaţii precum cautare, comparare, concaternare.
› Se doreşte ca acest comportament să poate fi aplicat mai multor tipuri de
carcatere: char, unsigned char, caractere chinezeşti, caractere greceşti etc.
› Cu alte cuvinte se doreşte reprezentarea noiţiunii de „şir de caractere” cu o
dependeţă cât mai mică de un specific tip de caracter
C++ String template
› Fie clasa String:
class String
{
char *ptr;
int sz;
public:
String() : sz(0), ptr(0) {};
explicit String(const char*p);
String(const String&);
char& operator[](int n) { return ptr[n]; }
String& operator+=(char c);
String& operator=(const String&);
˜String() { if (ptr) delete[] ptr; }
};

› Clasa gestionează şiruri de caractere de tip char.


› Pentru a deveni o clasă generală se parametrizează tipul de date
C++ String template
template<typename T>
class String {
T *ptr;
int sz;
public:
String(void) : sz(0), ptr(nullptr) {};
explicit String(const T*p);
String(const String<T>&);
T& operator[](int n) { return ptr[n]; }
String<T>& operator+=(T c);
String<T>& operator=(const String<T>&);
String(String<T>& x);
~String(void) { if (ptr) delete[] ptr; }
};
› Prefixul template<typename T> specifică faptul că un template este
declarat şi că tipul argument T va fi folosit în declaraţie
› După introducere, tipul T este utilizat ca oricare alt tip de date
C++ String template
› Domeniul tipului de date T se extinde până la finalul declaraţiei prefixate de
template<typename T>.
› Poate fi utilizat şi prefixul echivalent template<class T>
› În ambele cazuri T este un nume de tip de date şi nu numele unei clase.
› Utilizare: String<char> cs;
String<unsigned char> us;
String<wchar_t> ws;

struct jchar { /* ... */ }; // caractere nipone

String<jchar> js;

› Cu excepţia sintaxei speciale a numelui, obiectul String <char> cs are


aceeaşi funcţionalitate ca cea definită anterior.
› Făcând String un template permite furnizarea facilitărilor implementate
pentru orice tip de caracter
C++ String template
› Libraria standard oferă clasă template pentru manipularea sirurilor de
caractere sub numele de basic_string similar clasei templetizate
prezentate în curs.
› În librăria standard, string este un sinonim pentru basic_string<char>.
using string = std::basic_string<char>;

› Astfel pentru secvenţa basic_string<char> sir; poate fi utilizat string


sir;
› În general aliasurile sunt utile pentru evita denumirile lungi ale claselor
generate. De asemenea se preferă a nu se cunoaşte detaliile despre cum
un tip este definit. Un alias permite ascunderea faptului că un tip este
generat dintr-un template
C++ Definirea unui template
› O clasă generată dintr-un template este o clasă normală. Din acest motiv
utilizarea unei clase template nu implică nici un mecanism run-time
comparativ cu o clasă normală.
› Utilizarea template-urilor conduce la o descreştere a codului generat
deoarece codul pentru o funcţie membră a unei clase template este
generată dacă acel membru este utilizat.
› Înainte de a proiecta o clasă template este preferabil de a proiecta, testa,
depana o clasă concretă (String). Astfel o serie de erori de implementare
pot fi eliminate în contextul exemplului concret.
› Pentru a putea înţelege generalitatea unui template se va imagina
comportarea acestuia pentru un timp concret de date.
› Pe scurt: o componentă generică este dezvoltată ca o generalizare a unui
sau mai multe exemple concrete şi nu pornind de la principii
C++ Definirea unui template
› Membri unei clase template sunt declaraţi şi definiţi exact ca la clasele
nontemplate
› Totuşi când un membru este declarat în afara clasei trebuie în mod explicit
declarat template-ul
template<typename T>
String<T>::String() : sz{0}, ptr{ch}
{
ch[0] = {};
}

template<typename T>
String<T>& String<T>::operator+=(T c)
{
// ... concaternare
return ∗this;
}
C++ Definirea unui template
› Parametrul template T este un parametru şi nu un nume pentru un tip
specific. În domeniul String<T>, identificatorul <T> este numele template-
ului. Astfel numele constructorului este String<T>::String.
› Nu este posibilă supraîncărcarea numelui unei clase template.

template<typename T>
class String { /* ... */ };

class String { /* ... */ }; // eroare

› Un tip utilizat ca template trebuie să furnizeze interfaţa aşteptată de


template
C++ Instanţierea unui template
› Procesul de generare a unei clase ori funcţii dintr-o listă de argumente
template este denumit adesea template instantiation. O versiune a template-
ului pentru o listă specifică de argumente este numită specializare.
String<char> cs;

void f(void)
{
String<jchar> js;
cs = ”Abcd efg hgi";
}

› Pentru secvenţa de mai sus compilatorul generează declaraţia claselor


String<char> şi String<jchar>, pentru fiecare în parte destructori şi
constructori cu listă vidă de parametri şi funcţia operator
String<char>::operator=(char∗)
› Alte funcţii membre nefiind folosite nu sunt generate.
C++
› Evident, template-urile furnizează o modalitate de a genera foarte mult
cod din relativ mici definiţii. În consecinţă o atenţie sporită trebuie avută
pentru a nu umple memoria cu definiţii de funcţii aproape identice.
C++ Un template poate avea mai mult de un argument
#include<iostream>
using namespace std;

template<class T, class U>


class A
{
T x;
U y;
Public:
A(void) { cout<<“Apel constructor"<<endl; }
};

int main(void)
{
A<char, char> a;
A<int, double> b;
return 0;
}
C++ Pot exista argumente predefinite pentru template
#include<iostream>
using namespace std;

template<class T, class U = char>


class A
{
public:
T x;
U y;
A(void) { cout<<“Apel constructor"<<endl; }
};

int main(void)
{
A<char> a; // Apel constructor ce apartine clasei
// A<char, char>
return 0;
}
C++ We can pass non-type arguments to templates
template <class T, int max>
int arrMin(T arr[], int n)
{
int m = max;
for (int i = 0; i < n; i++)
if (arr[i] < m)
m = arr[i];

return m;
}
int main()
{
int arr1[] = {10, 20, 15, 12};
int n1 = sizeof(arr1)/sizeof(arr1[0]);

char arr2[] = {1, 2, 3};


int n2 = sizeof(arr2)/sizeof(arr2[0]);

// Second template parameter to arrMin must be a constant


cout << arrMin<int, 10000>(arr1, n1) << endl;
cout << arrMin<char, 256>(arr2, n2);
return 0;
}
C++ Functii template si variabile statice
template <typename T>
void fun(const T& x)
{
static int i = 10;
cout << ++i;
}
int main()
{
fun<int>(1); // tipareste 11
cout << endl;
fun<int>(2); // tipareste 12
cout << endl;
fun<double>(1.1); // tipareste 11
cout << endl;
return 0;
}
C++ Clase template si variabile statice
#include <iostream>
using namespace std;

template <class T> class Test


{
private:
T val;
public:
static int count;
Test(){ count++; } // some other stuff in class
};

template<class T>
int Test<T>::count = 0;

int main()
{
Test<int> a; // valoarea count pentru Test<int> este 1
Test<int> b; // valoarea count pentru Test<int> este 2
Test<double> c; // valoarea count pentru Test<double> este 1
cout << Test<int>::count << endl; // tipareste 2
cout << Test<double>::count << endl; // tipareste 1
return 0;
}
C++ Template specialization
template <class T>
void fun(T a)
{
cout << "The main template fun(): " << a << endl;
}

template<>
void fun(int a)
{
cout << "Specialized Template for int type: " << a << endl;
}
int main()
{
fun<char>('a');
fun<int>(10);
fun<float>(10.14);
}
C++ Template specialization
#include <iostream>
using namespace std;

template <class T>


class Test
{
// Data memnbers of test
public:
Test()
{
// Initializstion of data memnbers
cout << "General template object \n";
}
// Other methods of Test
};
C++ Template specialization
template <>
class Test <int>
{
public:
Test()
{
// Initializstion of data memnbers
cout << "Specialized template object\n";
}
};

int main()
{
Test<int> a;
Test<char> b;
Test<float> c;
return 0;
}
C++ Derivare
template<typename T>
class B
{
/* ... */
};

template<typename T>
class D : public B<T>
{
/* ... */
};

template<typename T> void f(B<T>∗);


void g(B<int>∗ pb, D<int>∗ pd)
{
f(pb); // f<int>(pb)
f(pd); // f<int>(static_cast<B<int>*>(pd));
// conversie standard D<int>* to B<int>*
}
C++ Sfaturi
› A se utiliza template-uri pentru
– a implementa algoritmi ce se adresează mai multor tipuri de date
– a implementa containere
› Cele două declaraţii template<typename T> şi template<class T> sunt
sinonime
› Înainte de a proiecta un template se va proiecta şi implementa o versiune
nontemplate. Ulterior se va generaliza parametrizând tipul/tipurile de date
› O funcţie virtuală nu poate fi o funcţie template membră
› A se supraîncărca funcţiile template pentru a se obţine aceeaşi semantică
pentru mai multe tipuri de argumente ()
› A se utiliza aliasuri de template-uri pentru a simplifica notaţia şi a ascunde
detaliile de implementare
› A se include definiţiile template în orice fisier sursă unde este necesar

You might also like