- •Programare orientată pe obiecte
- •§1. Principiile programării orientate pe obiecte.
- •§3. Constructori şi destructori.
- •8) Aceste mesaje apar după ce se termină lucrul programului.
- •§4. Operaţii de intrare/ieşire a informaţiei.
- •§5. Moştenire simplă.
- •§6. Moştenire multiplă
- •§7. Moştenirea pe mai multe niveluri. Clase virtuale
- •§9. Definirea şi utilizarea referinţelor
- •§10. Tablouri de obiecte. Pointeri şi referinţe la obiecte. Pointeri la membrii clasei
- •§11. Dirijarea dinamică a memoriei
- •§12. Constructor de copiere
- •§13. Funcţii prietene şi clase prietene
- •§14. Supraîncărcarea operatorilor
- •§15. Supraîncărcarea operatorilor prin funcţii prietene
- •§16. Supraîncărcarea unor operatori speciali
- •Operatorii de incrementare şi decrementare
- •Operatorii de inserţie şi extragere
- •Operatorul indice
- •Operatorul funcţie
- •Operatorii new şi delete
- •Operatorul virgulă
- •Operatorul de conversie
- •§17. Funcţii-şablon şi clase-şablon
- •§18. Realizarea conceptului de polimorfism
- •§19. Clase abstracte
- •§20. Membrii statici ai clasei
- •§23. Tratarea excepţiilor
- •Bibliografie
§17. Funcţii-şablon şi clase-şablon
Funcţiile-şablon sunt concepute pentru a uşura procesul de creare a unor funcţii care realizează algoritmi similari, deosebindu-se doar prin tipul datelor prelucrate.
Să considerăm următorul exemplu. Să se realizeze două funcţii: una permite găsirea elementului maximal al unui tablou de numere întregi, iar alta permite găsirea elementului maximal al unui tablou de numere cu virgulă mobilă.
Presupunând că tabloul de numere întregi este de tip int, iar tabloul de numere cu virgulă mobilă este de tip float, sunt propuse următoarele funcţii:
pentru tabloul de numere întregi:
int el_max(int *tb, int n_el)
{
int max=tb[0];
for (int i=1; i< n_el ; i++)
if (max < tb[i]) max=tb[i];
return max;
}
pentru tabloul de numere cu virgulă mobilă:
float el_max (float *tb, int n_el)
{
float max=tb[0];
for (int i=1; i< n_el ; i++)
if (max < tb[i]) max=tb[i];
return max;
}
Din compararea acestor funcţii se poate observă că ele sunt identice prin setul de instrucţiuni utilizate şi se deosebesc doar prin tipurile unor variabile locale sau ale unor parametri fictivi. Pentru a descrie mai simplu astfel de algoritmi se utilizează funcţiile-şablon. Limbajul C++ are încorporat în el mecanismul funcţiilor-şablon. Ele se mai numesc şi funcţii parametrizate sau funcţii generice. Prin intermediul unei singure descrieri a funcţiei-şablon este realizată, de fapt, descrierea unei mulţimi de funcţii, câte una pentru diferite combinaţii de tipuri de date. O funcţie-şablon este descrisă în felul următor:
template
<class Ta1, class Ta2, . . ., class Tak>
[tip_r] nume_functie([lista_par_fictivi])
{
. . .
//instructiune
. . .
}
unde template este cuvântul-cheie; Ta1, Ta2, …, Tak sunt o serie de tipuri abstracte de date, iar celelalte elemente ale funcţiei sunt cunoscute din descrierea funcţiilor obişnuite. Tipurile abstracte de date pot fi utilizate ca descriptori pentru descrierea parametrilor fictivi sau a variabilelor locale. Tipul valorii returnate de funcţie poate fi la necesitate descris cu ajutorul unuia dintre tipurile abstracte. Revenind la exemplul precedent, în conformitate cu descrierea generală este obţinută următoarea funcţie-şablon:
template
<class Tip>
Tip el_max (Tip *tb, int n_el)
{
Tip max;
max=tb[0];
for (int i=1 ; i < n_el ; i++)
if (max < tb[i]) max =tb[i];
return max;
}
Din funcţia-şablon este obţinută funcţia reală necesară. Funcţiile reale se obţin la etapa compilării prin înlocuirea tipurilor abstracte de date prin tipuri reale de date. Un procedeu corect cere şi existenţa unui prototip care descrie funcţia reală necesară. Dacă funcţia-şablon este descrisă mai sus de locul utilizării, atunci prototipul funcţiei reale poate să lipsească. Iată forme de utilizare a funcţiei-şablon din exemplul anterior:
. . .
void main()
{
int el_max(int * , int);
float el_max(float *, int);
int ti[4]={-240,4,330,7};
float tf[3]={1.2, -3.47, 27.5};
double td[4]={0,1.12,2.45,3.27};
cout << el_max(ti, 4)<< endl;
cout << el_max(tf, 3)<< endl;
cout << el_max(td, 4)<< endl;
}
Nu totdeauna algoritmul descris de funcţia-şablon este acel care poate fi aplicat absolut pentru toate tipurile reale de date utilizate în program. Unele date de anumite tipuri pot necesita algoritmi diferiţi. În asemenea situaţii, pe lângă funcţii-şablon sunt admise şi funcţii obişnuite, care au acelaşi nume cu funcţiile-şablon şi corespund algoritmilor diferiţi. Dacă există şi funcţii-şablon şi funcţii obişnuite cu acelaşi nume, prioritare sunt funcţiile obişnuite elaborate pentru unele combinaţii de date de anumite tipuri concrete. De exemplu, suma a două variabile de tip char ar putea însemna un şir de caractere constând din valorile acestor variabile, ceea ce se deosebeşte desigur de suma a două variabile numerice. Pentru variabile numerice de tip int, short, long, float etc., este realizată o funcţie-şablon, iar pentru variabile de tip char este propusă o funcţie obişnuită, care realizează algoritmul necesar:
variabile numerice:
template
<class Ta>
T suma (Ta x, Ta y)
{
return (x+y);
}
variabile de tip char:
char* suma (char x, char y)
{
char *sir;
sir = new char[3];
sir[0] = x;
sir[1] = y;
sir[2] = ’\0’;
return sir;
}
Trebuie de subliniat faptul că funcţiile-şablon pot fi supraîncărcate tot aşa ca şi funcţiile obişnuite.
Dacă până acum au fost propuse exemple de funcţii-şablon care conţineau doar un singur tip abstract, în continuare este prezentat un exemplu conţinând mai multe tipuri abstracte.
Exemplu. De alcătuit o funcţie-şablon care permite compararea unei valori cu suma elementelor unui tablou.
template
<class Ta1, class Ta2>
int compar_suma (Ta1 *tablou, Ta2 val, int numEl)
{
int i;
Ta1 suma=0;
for(i=0; i<numEl; i++)
suma+=tablou[i];
return (val-suma);
}
Un alt mecanism ce uşurează mult procesul de proiectare şi implementare a claselor este mecanismul claselor-şablon. Clasele-şablon permit crearea unei mulţimi de clase asemănătoare după funcţionalitate care se deosebesc doar după tipul datelor prelucrate. Toate funcţiile membre ale unei clase-şablon sunt funcţii-şablon. Pentru a descrie o clasă-şablon, este utilizată următoarea schemă generală:
template
<class Ta1, class Ta2, …, class Tak>
class nume_cl_sb
{
. . .
// membri ai clasei
. . .
};
unde Ta1, Ta2, …., Tak sunt tipuri abstracte de date, care pot servi la descrierea tipurilor date-membre, a tipurilor valorilor returnate de funcţiile-membre, a tipurilor parametrilor fictivi, a tipurilor variabilelor locale ale funcţiilor membre. Pentru descrierea funcţiilor membre ale unei clase-şablon este utilizat modelul ce urmează:
template
<class Ta1, class Ta2, …, class Tak>
tip_r nume_cl_sb<Ta1, Ta2, …, Tak>::nume_fn(lista_pf)
{
. . .
//instructiuni
. . .
}
unde nume_fn este o oarecare funcţie-membră, iar lista_pf reprezintă lista parametrilor fictivi. Pentru a crea obiecte în baza unei clase-şablon, mai întâi tipurile abstracte sunt înlocuite cu tipuri concrete de date, obţinând o clasă care prelucrează date de tipuri concrete, iar apoi este aplicat în mod obişnuit constructorul clasei pentru a finaliza acţiunea:
nume_cl_sb<Tc1, Tc2, …, Tck> nume_ob(lista_pr);
unde Tc1, Tc2, …, Tck sunt tipuri concrete de date, nume_ob reprezintă numele obiectului creat, iar lista_pr este lista parametrilor reali.
Exemplu. De alcătuit un program în care este realizată clasa-şablon stiva, creând mai apoi stive concrete de diferite tipuri.
template
<class T>
class stiva
{
T *stv ; // stiva
int dim; // dimensiunea stivei
int vst; // varful stivei
public:
stiva(int d);
~stiva();
T push (T el);
T pop ( );
friend ostream & operator << (ostream & fl, stiva st);
};
//------------
template
<class T>
stiva <T> :: stiva (int d)
{
stv=new T[d];
if(stv==NULL)
dim=0;
else
dim=d;
vst=0;
}
//------------
template
<class T>
stiva <T> :: ~stiva ()
{
if(stv !=NULL)
delete [] stv;
}
//------------
template
<class T>
T stiva <T> :: push (T el)
{
if (vst<dim)
{
stv[vst]=el;
vst ++;
return el;
}
else
return –1;
}
//------------
template
<class T>
T stiva <T> :: pop ( )
{
if (vst>0)
{
vst -- ;
return stv[vst];
}
else
return –1;
}
//------------
template
<class T>
ostream & operator <<(ostream &fl, stiva <T> st)
{
for (int i=0; i<st.vst; i++)
fl << st.stv[i] << ’ ’ ;
return fl;
}
Memorând implementarea clasei în fişierul stiva_p.hpp, includem acest fişier în programul principal, efectuând o serie de operaţii cu diferite tipuri de stive:
#include <iostream.h>
#include "stiva_p.hpp"
void main ()
{
stiva<int> si(10);
stiva<long> sl(15);
stiva<float> sf(20) ;
for (int i=0; i<10; i++)
{
si.push(i);
sl.push(i+100000);
sf.push(float i/10.0);
}
cout << si << endl;
cout << sl << endl;
cout << sf << endl;
si.pop() ;
cout << si << endl ;
}
Remarcă: Stiva generică realizată este orientată la descrierea stivelor pentru tipuri numerice de date.
