Sunteți pe pagina 1din 10

abloane (Tamplate)

Folosind cuvntul cheie template se pot crea abloane pentru funcii i clase, numite funcii
generice, respectiv clase generice. ntr-o funcie sau clas generic tipul de date asupra cruia
opereaz acestea este specificat ca un parametru. Aceste funcii i clase se pot folosi cu diferite
tipuri de date fr a rescrie versiunile specifice fiecrui tip.

Funcii generice
O funcie generic definete un set de operaii care vor fi aplicate unor tipuri de date variate. n
definirea clasic a funciilor, pentru a transpune acelai algoritm mai multor tipuri de date, este
necesar rescrierea procedurii. Dac se definete o funcie generic, la execuia funciei,
compilatorul genereaz automat codul corect pentru tipul de date folosit efectiv. n esen, se
creeaz o funcie care se suprancarc singur automat.
Pentru crearea unei funcii generice se folosete cuvntul cheie template.
Sintaxa general folosit pentru definirea unei funcii generice este:
template < class Tip1, class Tip2,,class Tipn > tip_retur nume_functie (lista_parametri)
{
// corp funcie
}
Tipurile de date sunt precedate de cuvntul cheie class, ceea ce nu nseamn ns c pot fi doar
tipuri de date declarate cu class, ci pot fi orice tipuri de date.
Tip1,Tip2,...sunt nume care in locul unor tipuri de date folosite de funcie. Ele in doar
loc tipurilor de date pe care le va nlocui automat compilatorul la execuia funciei.
n exemplul urmtor este definit o funcie generic de inversare a valorilor a dou
variabile.
#include < conio.h >
#include < iostream.h >
template <class T> void inversare ( T & a, T & b)
{
T aux;
aux = a;
a = b;
b = aux;
}
class complex

{
double re, im;
public:
complex(double r = 0, double i = 0)
{ re = r; im = i;}
void afisare()
{ cout<<"(re="<<re<<" , im="<<im<<" )";}
};

void main()
{
int p = 7, q = 9;
float r = 1.2, s = -2.5;
complex c1( 3.1, -6.2 ), c2( r, s );
cout << "\np = " << p << " , q = " << q;
cout << "\nr = "<< r <<" , s = "<< s;
cout<<"\nc1: ";

c1.afisare();
cout <<" , c2: ";
c2.afisare();
inversare( p, q );
inversare( r, s );
inversare( c1, c2 );
cout << "\np = " << p << " , q = " << q;
cout << "\nr = " << r << " , s = " << s;
cout << "\nc1: ";
c1.afisare();
cout << " , c2: ";
c2.afisare();
getch();

Funcia generic inversare() preia referina a dou variabile de acelai tip, desemnat prin numele
T. La un apel al funciei se transmit dou date de tip int, la urmtorul de tip float, iar la al treilea
date de tip complex.
Un apel de forma: inversare( p , s ); va fi semnalat printr-un mesaj de eroare, deoarece variabilele
p i s sunt de tipuri diferite.
n exemplul urmtor este definit o funcie generic ce preia ca parametri dou tipuri de date,
funcia realiznd conversia ntre cele dou tipuri.
#include
#include
#include
#include

<stdio.h>
<conio.h>
<iostream.h>
<math.h>

template <class Tip1, class Tip2>


conv( Tip1 *t1, Tip2 *t2, int n)
{
for (int i = 0; i < n ; i++ )
*(t1 + i) = (Tip1)( *( t2 + i) );
}
class rational
{
int p, q;
public:
rational(int a=0, int b=1)
{ p = a; q = (b!=0 ? b : 1); }
rational(double d)
{ p = d*10; q = 10; }
void citeste();
void afisare(){cout << '(' << p << '/' << q << ')' ; }
operator double (){return (double)p / q ; }
};
void rational::citeste()
{ cout << "\np = ";
cin >> p;
do {
cout << "q = ";
cin >> q;
}
while(q == 0);
}
void main()
{
int t_i[5];
double t_d[5]={2.1 , 3.4 , 5.6 , 7.2 , -3};

rational t_r[5];
conv( t_i , t_d , 5 );
cout << endl;
for(int i=0 ; i<5 ; i++)
cout << t_i[i] << " ";
for(i=0 ; i<5 ; i++)
t_r[i].citeste();
conv( t_d , t_r , 5 );
cout << endl;
for( i=0 ; i<5 ; i++)
cout << t_d[i] << " " ;
conv( t_r , t_d , 5 );
cout << endl;
for(i=0 ; i<5 ; i++)
t_r[i].afisare();
getch();

n acest exemplu, funcia generica conv() poate fi apelata si pentru tablouri de tip rational datorit
faptului c sunt definite conversii de la tipul int la rational, de la tipul double la tipul rational, prin
constructorii clasei i conversia de la tipul rational la double prin funcia operator double();
n general, funciile generice pot prelua parametri de tipuri definite de utilizator n msura n care
conversiile i operatorii utilizai n definiia funciei sunt supradefinite pentru acele tipuri de date.
Dac o funcie generic nu acoper cerinele pentru un anumit tip de date, ea poate fi
supradefinit, dar cu restricia ca n lista de parametri s se regseasc toi parametrii din funcia
generic.
#include <iostream.h>
#include <string.h>
#include <conio.h>
typedef char string[20];
template <class T>
void ShellSort(T *a, int st, int fn)
{
int i, j, h;
T elem_sort;

h=1;
do { h = 4*h+1;} while (h < fn-st + 1);
do {
h /= 4;
for(i = st+h; i <= fn; i++)
{
elem_sort = a[i];
j = i;
while ( elem_sort < a[j-h] )
{
a[j] = a[j-h];
j -= h;
if ( j < st+h ) break;
}
a[j]=elem_sort;
}
} while (h>1);

//supradefinirea funciei pentru tablou de iruri de caractere

void ShellSort(string* a , int st , int fn)


{
int i, j, h;
string elem_sort;
h=1;
do { h = 3*h+1 ; } while ( h < fn st + 1) ;
do {
h /= 3;
for( i = st + h ; i <= fn ; i++ )
{
strcpy( elem_sort, a[i] );
j = i;
while ( strcmp( elem_sort , a[j-h] ) < 0 )
{
strcpy( a[j] , a[j - h] );
j -= h;
if ( j < st + h) break;
}
strcpy( a[j] , elem_sort );
}
}

} while ( h > 1);

void main()
{
clrscr();
int i_vect[25] = {6, 9, 22, 52, 27, 12, 99, 3, 14, 45, 11, 33,
5, 2, 67, 10, 18, 7, 73, 81, 13, 0, 4, 8, 1 };
ShellSort( i_vect , 0 , 24 );
for(int i = 0 ; i < 25 ; i++)
cout << i_vect[i] << " ";
cout << "\n";
string tab[10] ={ "aaa", "Abc", "bcds", "CFdad", "jkdwhksj",
"cmnd", "nksjklads", "jjjdklsa", "jdsjla", "jlkdej"};
ShellSort( tab , 0 , 9 );
for( i = 0 ; i < 10 ; i++)
cout << "\n" << tab[i];
getch();
}

n exemplul prezentat, este definit funcia generica void ShellSort(T *a,int st, int fn), care ns
nu poate fi folosit pentru tipul de date sir, pentru acesta fiind realizat supradefiniia void
ShellSort( string *a , int st , int fn ). Se observ c listele de parametri ale acelor definiii sunt
similare.

Clase generice
Sintaxa folosit pentru furnizarea parametrilor unei clase generice este:
template <par1, par2,>
Parametrii pot fi de dou categorii: tipuri de date sau constante. Tipurile de date sunt precedate de
cuvntul cheie class, ceea ce nu nseamn ns c pot fi doar tipuri de date declarate cu class, ci
pot fi orice tip de date. Parametrii care nu sunt precedai de cuvntul class sunt considerai
automat ca fiind constante.
4

De exemplu:
template <class Tip>
template <class Tip_el, long Dim>
template <int Dim1, int Dim2>
O clas template, numit i clas generic, permite definirea unui ablon pentru definirea de
tipuri de date. Declararea i definirea unei clase generice este totdeauna precedat de clauza
template.
Considerm urmtorul exemplu de clas generic, numit Stack.
template <class Type> class Stack;

Sintaxa de folosire a clauzei template este similar cu cea folosit pentru definirea funciilor
generice.
Definiia clasei este similar definirii normale, cu excepia c n interiorul definiiei clasei sunt
utilizai parametrii tipuri de date.
Definirea clasei Stack este dat n continuare.
template <class Type>
class Stack {
public:
Stack(int max) :stack(new Type[max]), top(-1), maxSize(max) {}
~Stack(void)
{delete [] stack;}
void Push
(Type &val);
void Pop(void)
{if (top >= 0) --top;}
Type& Top(void)
{return stack[top];}
friend ostream& operator << (ostream&, Stack&);
private:
Type *stack;
// stack array
int top;
// index of top stack entry
const int maxSize;
// max size of stack

};
Funciile member sunt definite inline, cu excepia funciei push(). Operatorul << este, de
asemenea, supradefinit pentru a afia coninutul stivei. Cele dou sunt definite astfel:
template <class Type>
void Stack<Type>::Push (Type &val)
{
if (top+1 < maxSize)
stack[++top] = val;
}
template <class Type>
ostream& operator << (ostream& os, Stack<Type>& s)
{
for (register i = 0; i <= s.top; ++i)
os << s.stack[i] << " ";
return os;
}

n afar de definiia propriu-zis, referirea la o clas template trebuie s includ lista de


parametric template. De aceea, definiiile acestor funcii conin numele Stack<Type> n loc de
Stack.
O clas template reprezint o form generic care n momentul implementrii va fi folosit pentru
crearea de tipuri concrete. Parametrii tipuri de date pot fi att tipuri fundamentale, ct i tipuri
definite de programator. De exemplu, se pot declara stive cu elemente de tipuri diferite, cum ar fi
int, pouble, point:
Stack<int>

s1(10);

// stiv cu elemente int

Stack<double>

s2(10);

// stiv cu elemente double

Stack<point>

s3(10);

// stiv cu elemente point

Fiecare dintre aceste instanieri genereaz funcii membre ale clasei corespunztoare fiecrei
declaraii. Astfel, putem scrie secvena:
s1.Push(10);
s1.Push(20);
s1.Push(30);
cout << s1 << '\n';

care va produce afiarea:


10 20 30

Cnd o clas sau funcie non-template refer o clas template, trebuie s existe declaraie clar a
tipului utilizat. De exemplu:
class Sample {
Stack<int>

intStack;

Stack<Type> typeStack;

// ok
// illegal! Type nu e definit

//...
};

n schimb declaraia :
template <class Type>
class Sample {
Stack<int>

intStack;

Stack<Type> typeStack;

// ok
// ok

//...
};

este corect.
Referirea Stack<int> reprezint un specificator valid pentru un tip de date i poate fi folosit ca
orice alt tip de date.
Nu numai tipuri de date pot reprezenta parametric pentru clase template, ci i constante. Iat o
alt definiie a clasei Stack , unde valoarea dimensiunii maxime a stivei este transferat ca
parametru template, n loc de a fi specificat ca dat membr a clasei.
template <class Type, int maxSize>
class Stack {

public:
Stack
(void) :stack(new Type[maxSize]), top(-1) {}
~Stack (void)
{delete [] stack;}
void Push
(Type &val);
void Pop(void)
{if (top >= 0) --top;}
Type&Top(void)
{return stack[top];}
private:
Type*stack;
// stack array
int top;
// index of top stack entry

};
Amndoi parametrii trebuie precizai la definirea funciilor. Iat de exemplu definiia funciei
push():
template <class Type, int maxSize>
void Stack<Type, maxSize>::Push (Type &val)
{
if (top+1 < maxSize)
stack[++top] = val;
}

Din pcate, operatorul << nu poate fi supradefinit ca nainte, deoarece parametrii nu pot fi
utilizai pentru funcii non-tamplate.
template <class Type, int maxSize>
ostream &operator << (ostream&, Stack<Type, maxSize>&);// illegal!

Instanierea clasei template Stack cere acum precizarea celor doi parametrii:
Stack<int, 10>

s1;

// ok

Stack<int, 10u> s2;

// illegal! 10u diferit de int

Stack<int, 5+5> s3;

// ok

int n = 10;
Stack<int, n>

s4;

// illegal! n primete valoare doar la execuie

In continuare este definit o clasa vector template cu doi parametri. Tip reprezint tipul
elementelor vectorului, iar DIM dimensiunea sa.
#include <conio.h>
#include <iostream.h>
template <class Tip, int DIM>
class vector
{
Tip tab[DIM];
public:
void cit();
void af();
Tip& operator[](int);
Tip operator ~();
// returneaz valoarea maxima a tabloului

};
template <class Tip, int DIM>
void vector<Tip,DIM>::cit()
{
int i;
for(i=0; i<DIM; i++)
{ cout<<"vector["<<i<<"]=";
cin>>tab[i];
}
}
template <class Tip, int DIM>
void vector<Tip,DIM>::af()
{
int i;
cout<<endl;
for(i=0; i<DIM; i++)
cout<<tab[i]<<" ";
}
template <class Tip, int DIM>
Tip& vector<Tip,DIM>::operator[](int i)
{
if (i<DIM)
return tab[i];
else
return tab[0];
}
template <class Tip, int DIM>
Tip vector<Tip,DIM>::operator ~()
{
int i;
Tip max=tab[0];
for(i=1;i<DIM;i++)
if (max<tab[i])
max=tab[i];
return max;
}
void main()
{
vector<int,6> v1;
vector<float,5> v2;
v1.cit();
v1.af();
cout<<endl<<v1[3];
cout<<endl<<~v1;
v2.cit();
v2.af();
cout<<endl<<v2[3];
cout<<endl<<~v2;
}

getch();

Spre deosebire de prima definiie, urmtorul exemplu definete o clasa vector template cu un
parametru, tipul elementelor vectorului, iar dimensiunea sa este stabilit prin membrul dim:
8

#include <conio.h>
#include <iostream.h>
template <class Tip>
class vector
{
Tip* tab;
int dim;
public:
vector(int=0);
~vector();
void cit();
void af();
Tip& operator[](int);
Tip operator ~();
};

// returneaz valoarea maxima a tabloului

template <class Tip>


vector<Tip>::vector(int n)
{
int i;
dim=n;
tab=new Tip[dim];
}
template <class Tip>
vector<Tip>::~vector()
{
delete tab;
}
template <class Tip>
void vector<Tip>::cit()
{
int i;
for(i=0; i<dim; i++)
{ cout<<"vector["<<i<<"]=";
cin>>tab[i];
}
}
template <class Tip>
void vector<Tip>::af()
{
int i;
cout<<endl;
for(i=0; i<dim; i++)
cout<<tab[i]<<" ";
}
template <class Tip>
Tip& vector<Tip>::operator[](int i)
{
if (i<dim)
return tab[i];
else
return tab[0];
}
template <class Tip>
Tip vector<Tip>::operator ~()

int i;
Tip max=tab[0];
for(i=1;i<dim;i++)
if (max<tab[i])
max=tab[i];
return max;

void main()
{
vector<int> v1(6);
vector<float> v2(4);
v1.cit();
v1.af();
cout<<endl<<v1[3];
cout<<endl<<~v1;
v2.cit();
v2.af();
cout<<endl<<v2[3];
cout<<endl<<~v2;
getch();
}

10

S-ar putea să vă placă și