Sunteți pe pagina 1din 10

Funcii i clase generice (parametrizate).

1. 2. 3. 4. 5. Polimorfism parametric. Polimorfism prin suprancrcarea funciilor (polimorfism ad-hoc). Funcii polimorfice prin conversie de tipuri. Funcii generice. Clase generice.

1. Polimorfism parametric. O entitate polimorfic poate avea mai multe tipuri (polimorfism = mai multe forme). Polimorfismul parametric se refer la posibiltatea apelrii unei funcii cu un numr variabil de parametri. Un exemplu tipic n acest sens l reprezint funciile de intrare-ieire: printf, fprintf, scanf, fscanf, etc. Polimorfismul parametric este posibil n C i C++, deoarece la apelul funciilor parametrii sunt pui n stiv de ctre programul apelant. n C, funciile definite fr nici un parametru (nici mcar void) nu sunt verificate de ctre compilator, aa c pot fi apelate cu orici parametri, eventual de tipuri diferite. n C++, pentru a specifica un numr variabil de parametric vom defini n mod explicit o funcie cu numr variabil de parametric, prin tipul (. . .), specificat ntotdeauna ca ultim parametru. Parametrii pui n stiv de ctre programul apelant vor trebui prelucrai n mod explicit de ctre programator. n acest scop se utilizeaz macroinstruciunile va_start, va_arg i va_end definite n fiierul <stdarg.h>. O funcie cu numr variabil de parametri va avea prototipul: tip nume (list_fix_parametri, . . .); Lista fix de parametri trebuie s fie nevid, deci numrul de parametri va fi mai mare sau egal cu numrul de parametri fici. Parametrii care sunt n numr variabil sunt convertii implicit ca tip, i anume: toi ntregii la int toi realii la double Fiierul antet <stdarg.h> conine definiii pentru tipul va_list. Argumentele variabile vor fi accesate printr-o variabil pointer pa, declarat astfel: va_list pa; Iniializarea pointerului de acces la argumentele variabile - pa se face folosind macroinstruciunea va_start() , indicnd adresa ultimului parametru fix lastarg: va_start(pa, lastarg); Pentru parcurgerea listei de argumente variabile se va folosi macroinstruciunea va_arg(), care actualizeaz pointerul de acces la argumente pa, pentru a indica urmtorul argument int sau double, i ntoarce ca rezultat argumentul curent din lista de parametri variabili: 1

vint=va_arg(pa, int); sau vreal=va_arg(pa, double); Oprirea procesului repetitiv se face folosind informaiile despre parametrii fici (n vrful stivei se va afla pointerul la format). Dup ultimul parametru variabil extras se apeleaz macroinstruciunea: va_end(pa); Exemplul: Scriei o funcie care afieaz un numr variabil de iruri de caractere (cel mult max). #include <stdio.h> #include <stdarg.h> int printvar(int max, ); void main(void) { printvar(3,Ion,Vasile,Mihai); printf(\n); printvar(5,marti,joi,luni,vineri,duminica); printf(\n); } void printvar(int max,) { va_list pa; int narg=0; char *siruri[10]; va_start(pa,max); while(narg < max) { siruri[narg]=va_arg(pa, char*); printf(%s \n, siruri[narg++]); } va_end(pa); } Extragerea argumentelor variabile, poate fi fcut, i cu alte funcii, n loc de va_arg(). n acest scop se folosesc funciile: vprintf(), vfprintf() i vsprintf(). Acestea au prototipurile: int vprintf(char * format, va_list pa);

afieaz, sub controlul formatului, la ieirea standard, un numr variabil de


argumente, accesate prin pointerul pa ntoarce numrul de octei afiai (rezultat negativ la eroare) Exemplu: #include <stdio.h> #include <stdarg.h> int printvar(char* fmt, ); void main(void) { fmt1[]=%s %s %s\n; printvar(fmt1,Ion,Vasile,Mihai); } void printvar(char* fmt,) { va_list pa; 2

va_start(pa,fmt); vprintf(fmt,pa); va_end(pa); } int vfprintf(FILE * fis, char * format, va_list pa);

afieaz, sub controlul formatului, n fiierul fis, un numr variabil de


argumente, accesate prin pointerul pa ntoarce numrul de octei afiai (rezultat negativ la eroare) Exemplu: #include <stdio.h> #include <stdarg.h> #define NUMEFIS fis.dat void printvar(FILE* f, char* fmt, ); void main(void) { FILE* f1; fmt1[]=%s %s %s\n; f1=fopen(NUMEFIS, w); printvar(f1,fmt1,Ion,Vasile,Mihai); fclose(f1); } void printvar(FILE* f, char* fmt,) { va_list pa; va_start(pa,fmt); vfprintf(f, fmt, pa); va_end(pa); } int vsprintf(char * sir, char * format, va_list pa);

afieaz, sub controlul formatului, n irul de caractere sir, un numr variabil de


argumente, accesate prin pointerul pa Exemplu: #include <stdio.h> #include <stdarg.h> int printvar(char* s, char* fmt, ); void main(void) { fmt1[]=%s %s %s\n; char s[100]; printvar(s, fmt1, Ion,Vasile,Mihai); printf(%s,s); } void printvar(char* s, char* fmt,) { va_list pa; va_start(pa,fmt); vsprintf(s,fmt,pa); va_end(pa);

} Exemplul : Scriei o funcie cu numr variabil de parametri, care simuleaz funcia printf(), acceptnd parametri variabili de tip int, double sau ir de caractere. #include <stdio.h> #include <stdarg.h> void printvar(char* fmt,) { va_list pa; char *p, *psir; int i; double d; va_start(pa,fmt); for (p=fmt; *p; p++) { if(*p!=%){ putchar(*p); continue; } switch(*++p) { case d: i=va_arg(pa, int); printf(%d,i); break; case f: d=va_arg(pa, double); printf(%lf,d); break; case s: for (psir=va_arg(pa,char*);*psir;psir++) putchar(*psir); break; default: putchar(*p); break; } } va_end(pa); } 2. Polimorfism prin suprancrcarea funciilor (polimorfism ad/hoc). Dac se consider funcia ca un tip, suprancrcarea funciilor reprezint o form de polimorfism numit polimorfism ad-hoc; funciile suprancrcate au acelai nume dar semnturi diferite i cod diferit. Selecia funciei se face dup parametrii din apel. De exemplu declararea mai multor versiuni ale funciei sqrt(): double sqrt(double); long sqrt(long);

3. Funcii polimorfice prin conversie de tipuri. Tot polimorfism ad-hoc reprezint conversia implicit ntre tipul pointer generic (pointer la void) i pointer cu tip. O funcie polimorfic, n aceast accepiune, va avea ca parametri pointeri generici . n apelul funciei, acetia vor fi nlocuii prin pointeri la tipuri definite. Exemplificm prin definirea unei funcii polimorfice de cutare binar. Pointerii generici nu pot fi derefereniai, motiv pentru care convertim n pointeri la octet. typedef unsigned char OCTET; typedef int (*PFCP)(void* ch, void* el); void* CautBin(void* ch, void* tb, int n, int dim, PFCP fcp){ int rezcp; OCTET *min = (OCTET*) tb; OCTET *med; OCTET *max = (OCTET*) tb + (n-1)*dim; while(min <= max){ med = min + (max-min)/dim/2*dim; rezc = (*fcp)(ch, med); if(rezcp < 0) max = med dim; else if(rezcp > 0) min = med + dim; else return med; }; return NULL; } Pointerii la elementele de acelai tip pot fi comparai (min <= max). Pointerul med se obine pe o cale mai ocolit: se adun la pointerul min o valoare constant distana ntre min i max, exprimat n octei (max-min) este convertit mai nti n indice, prin mprirea cu dimensiunea elementului dim, este apoi mprit la 2, pentru a obine mijlocul i transformat din nou n octei. Funcia de comparaie are ca parametrii pointeri generici la valorile comparate i ntoarce o valoare negativ, 0 sau o valoare pozitiv, n funcie de rezultatul comparaiei. Ea va fi adresat printr-un pointer (pointer la funcie) . Explicitarea funciei concrete de comparaie se va face n apel. Vom considera dou cazuri: compararea a dou valori ntregi, respectiv compararea a dou iruri de caractere. Pointerii generici sunt convertii n pointeri la tipul respectiv (ntreg sau ir de caractere), se face dereferenierea i se returneaz ca rezultat diferena valorilor, respectiv rezultatul comparaiei irurilor de caractere. int cpint(void* ch, void* el){ return *((int*)ch) - ((int*)el);

}; int cpsir(void* ch, void* el){ return strcmp((char*) ch, *((char**)el)); }; 4. Funcii generice. Exist multe funcii (i clase) nrudite ntre ele, cu excepia unor tipuri. De exemplu o funcie care sorteaz un tablou de ntregi va diferi foarte puin de un algoritm de sortare a unui tablou de reali. Mecanismul abloanelor (templates) creaz funcii sau clase generice (parametrizate) utiliznd tipurile ca parametri. Un ablon definete o familie de funcii, respectiv de clase.Aceste tipuri se specific la definire, n mod generic, urmnd apoi instanierea tipurilor generice cu tipuri concrete. Mecanismul reutilizeaz codul surs, prin expandare, n condiiile verificrii complete a tipurilor. O entitate template descrie un ablon pentru un numr nespecificat de entiti concrete, nrudite ntre ele. De exemplu, pentru calculul diferenei n valoare absolut a dou valori ntregi, reale sau ntregi lungi s-ar putea scrie o varietate de funcii suprancrcate, care ar avea acelai cod, diferind doar semntura (tipul parametrilor, nu i numrul lor). int max(int x, int y){ return x y > 0? x : y; } long max(long x, long y){ // identic } float max(float x, float y){ // identic } Toate variantele ar putea fi comasate ntr-una singur, avnd tipul parametrizat: template <class T> T max(T x, T y){ return x - y > 0? x : y; } Definirea unei funcii generice cuprinde: template <par1, par2,..., parn> declaraie funcie parametrizat; Parametrii din lista de parametri a ablonului pot fi tipuri de date, caz n care se specific sub forma class Ti sau constante. Parametrii din lista parametrilor ablonului (template) trebuie s apar toi n lista de parametri formali a funciei generice.

Instanierea funciei parametrizate se face prin: nume_funcie_parametrizata(expr1, expr2,...,exprn) unde expr1, expr2,...,exprn sunt expresii din care se deduc tipurile concrete. Tipul generic T poate fi instaniat cu orice tip concret, inclusiv un tip definit de utilizator, care suport operatorii - i > i care poate fi ntors de ctre funcie. Compilatorul suport funcia generic T max(T, T) prin generare de cod surs pentru o funcie C max(C, C), unde C este tipul concret al parametrilor de apel. Generarea de cod pentru entitatea template are loc la compilare. Parametrii ablonului pot fi tipuri predefinite ,tipuri definite de utilizator (clase) sau constante. La apelul funciei parametrizate, tipul argumentului determin care versiune a ablonului este folosit. De exemplu max(10, 25) selecteaz funcia int max(int, int). Compilatorul deduce tipul argumentului din apel, dac reuete s le identifice unic, fr a efectua vreo conversie implicit. Astfel apelul max(10, 2.5) este ambiguu deoarece ar putea fi interpretat ca int max(int, int)sau float max(float,float) Situaiile de acest fel trebuiesc clarificate prin suprancrcare: float max(int x, float y){ return x-y>0? x : y; }; float max(float x, int y){ return x-y>0? x : y; }; Redefinirea funciei are prioritate asupra definiiei ablonului. Astfel avnd: template <class T> T min(T a, T b){ return (a < b)? a : b; } i char* min(char* s1, char* s2){ return (strcmp(s1,s2) < 0? S1 : s2); } pentru argumente iruri de caractere va fi aleas varianta suprancrcat a funciei, fr a se ncerca potrivirea cu ablonul. Funciile membre ale unei clase generice, sunt, la rndul lor funcii generice, adic pot fi aplicate unor tipuri de date diferite. n afara acestora se pot defini i funcii nemembre generice reprezentnd familii de funcii. Fie funcia generic: template <class T> T minim(const T* x, int n){ T xm = x[0]; for(int i=1; i<n; i++) if(x[i]<xm) 7

xm = x[i]; return xm; }; Instanierea ablonului,, adic nlocuirea parametrilor ablonului cu parametrii efectivi are forma: int ix[] = { 3, 8, 1, 5, 13, 7}; float fx[] = {1.5, -3.2, 7.3, 4.8}; void main(){ int n = sizeof(ix) / sizeof(int); int imin = minim(ix, n); n = sizeof(fx) / sizeof(float); float fmin = minim(fx, n); . . . } Toate funciile generate pornind de la o funcie generic sunt versiuni suprancrcate ale uneia dintre ele. Mecanismul funciilor generice poate fi deci privit ca un mijloc de realizare a polimorfismului ad-hoc. 5. Clase generice. Clasele colecii conin obiecte de un tip particular (de exemplu o list nlnuit de ntregi sau un tablou de structuri. Se pot defini familii de clase colecii, membrii acestora diferind numai prin tipul elementelor. Considerm clasa tablou de ntregi: pentru a folosi un tablou de reali, de compleci sau de iruri de caractere s-ar putea copia implementarea clasei, modificnd tipul datelor i numele clasei. Astfel am avea clasele intArray, StringArray, ComplexArray, etc. Familia de clase poate fi reprezentat printr-o clas generic (parametrizat). Aceasta specific modul n care pot fi construite clasele individuale, care se deosebesc numai prin tipul elementelor pe care le conin. Instanierea unui ablon reprezint generarea declarrii unei clase dintr-o clas parametrizat i un argument generic. O versiune a ablonului pentru un argument particular poart numele de specializare. Utilizarea unei clase generice presupune generarea de ctre compilator a fiecrei clase individuale, corespunztor tipurilor care instaniaz clasa generic. O clas generic se reprezint astfel: template <list_argumente_generice> declarare_clas; Instanierea unei clase generice se face prin: nume_clas <list_argumente_concrete> nume_obiect; Definim clasa generic (parametrizat): template <class T> class Array{ public: Array(int d=10): a(new T[dim]), dim(d){}

~Array(){delete a;} private: T *a; int d; }; Instanierea clasei ablon se face prin: Array<int> x; Array<Complex> c; Array<String> s; Definirea unei clase ablon se face prin: template <lista_parametri_sablon> declaratie sau definitie clasa; Fiecare parametru din ablon reprezint un tip: a. definit de utilizator: class T b. predefinit: int, double, etc Exemplu template <class T, int d> class Buffer{ T a[d]; int dim; public: Buffer() : dim(d){} }; i instanieri de forma: Buffer<char, 63> bufc; Buffer<Rec, 10> bufr; cu: class Rec{ char s[10]; }; O funcie generic de cutare n bufferul generic a unui ir va avea semntura: template <class T, int n> T& cautare(Buffer<T,n> &b, const char* p); Funciile generice pot fi suprancrcate, cu condiia ca instanele s aib semnturi diferite. De exemplu: template <class T> class Array{. . .}; template <class T> T suma(Array<T>, int); template <class T> T suma(T*, int); //clasa generica //functie generica //functie generica supraincarcata

void main(){ Array<int> A[100]; //instantiere clasa generica int a[100]; int s1 = suma(A, 100);//instantiere functie generica int s2 = suma(a, 100);

10

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