CPP_RO_2_BAZELE C++

2.9. Func?ii generice

2.9.Func?ii generice

Supra?nc?rcarea func?iilor ne d? posibilitate s? folosim unul ?i acela?i nume de func?ie pentru diferite tipuri de date. Neajunsul totu?i este acela c? pentru fiecare tip trebuie s? scriem o noua defini?ie de func?ie, deoarece sunt dese cazurile c?nd unul ?i acela?i algoritm este potrivit pentru mai multe tipuri de date f?r? descrieri repetate.

O alt? posibilitate ?n C++ de a crea mai multe func?ii cu unul ?i acela?i nume este utilizarea ?abloanelor (engl. template) sau func?iilor generice.

 

Func?ia la declararea c?reia este indicat unul sau mai multe tipuri parametrizate, care vor fi concretizate ?n fiecare loc unde se apeleaz? acest? func?ie, se nume?te func?ie generic? (sau ?ablon, sau de tip template)

 

Forma general? de defini?ie a unei func?iei generice cu un singur tip parametrizat este urm?toarea:

 

template <class tip> tip_retur nume_func?ie(list?_parametri)

{

// Corpul func?iei

}

Observa?ie. Cuv?ntul-cheie class poate fi ?nlocuit cu cuv?ntul-cheie typename. Efectul va fi acela?i, ?ns? ?n cazul ?n care tipul parametrizat tip dup? sens reprezint? un tip simplu, defini?ia func?iei poate fi mai clar?. Aceast? observa?ie se refer? ?i la declararea func?iilor generice cu mai multe tipuri parametrizate,  ?i la declararea claselor generice cu unul sau mai multe tipuri parametrizate.

La apelul func?iei compilatorul folose?te un exemplar de func?ie generat ?n dependen?? de concretizarea tipului.

Concretizarea poate fi explicit? (prin indicarea <tip>)

nume_func?ie<tip>(list?_argumente)

sau implicit? (numai prin lista argumentelor)

nume_func?ie(list?_argumente)

?n calitate de tipul concret tip poate fi folosit orice tip cunoscut ?n locul apelului acestei func?ii.

Cercet?m urm?torul exemplu:

#include "iostream.h"
// afl? ?i returneaz? valoarea maxim? din cele dou?
template <class T> T max(T t1, T t2)
{
   return t1>t2 ? t1 : t2;
}
 
void main()
{
   int i=2, k=10;
   cout << "max(" << i << ", " << k << ")="
        << max(i, k) << endl;
 
   cout << "max(" << 3.14 << ", " << 2.25 << ")="
        << max(3.14, 2.25) << endl;
 
   char c; cin >> c;
}


?n func?ia main() se demonstreaz? apelurile func?iei generice. Programul afi?eaz? urm?torul text:

max(2, 10)=10
max(3.14, 2.25)=3.14

Textul afi?at arat? rezultatele de apelare a func?iei generice. Prelucr?nd apelurile func?iei generice max(i, k), max(3.14, 2.25), compilatorul va genera dup? ?ablon func?ii concrete ?n dependen?? de tipul parametrilor. Acest proces se mai nume?te exemplificarea sau instan?ierea func?iei generice.

Tipul parametrizat tip, indicat la defini?ia unei func?ii generice, este un identificator arbitrar, ?i poate fi folosit sub form? de tip, tip&, tip*, tip** ?n aceast? func?ie astfel:

-        la descriea tipului valorii de retur;

-        la descrierea tipurilor parametrilor;

-        la declararea variabilelor locale ?n corpul func?iei;

-        la operatorii sizeof, new, conversie de tip.

 

?n cazul c?nd corpul func?iei generice nu este potrivit pentru un tip oarecare, ea poate fi supra?nc?rcat? pentru acest tip.

Inser?m ?n programul precedent, ?nainte de func?ia main(), urm?torul cod de supra?nc?rcare a func?iei generice max():

char* max(char* s1, char* s2) // afl? ?irul maximal din 
{                             // dou?
   int i=0;
   while( (*(s1+i)!='\0') && (*(s1+i)==*(s2+i)) )
      i++;
   return *(s1+i)>*(s2+i)? s1 : s2;
}


Iar la func?ia main() ad?ug?m urm?torul fragment:

char *s1="abcd";
   char *s2="abd";
   cout << "s1=\"" << s1 << "\"" << endl;
   cout << "s2=\"" << s2 << "\"" << endl;
   cout << "max(s1, s2)=\"" << max(s1, s2) << "\""
        << endl;
   cout << "max(\"" << "abdc" << "\", \"" << "abcd"
        << "\")=\"" << max("abdc", "abcd") << "\""
        << endl;


Prelucr?nd apelurile max(s1, s2), max("abdc", "abcd")), compilatorul va apela func?ia care supra?ncarc? func?ia generic? ?i are antetul

char *max(char* s1, char* s2)

Pe ecran va fi afi?at:

s1="abcd"
s2="abd"
max(s1, s2)="abd"
max("abdc", "abcd")="abdc"

Dezvolt?nd ?n continuare exemplul nostru, mai supra?nc?rc?m de dou? ori funcia generic? max(), respectiv cu trei ?i patru parametri:

template <class T> T max(T t1, T t2, T t3)
{
   return max(max(t1, t2), t3);
}
 
template <class T> T max(T t1, T t2, T t3, T t4)
{
   return max(max(t1, t2, t3), t4);
}


Exerci?iul 2.1. Modifica?i de sine st?t?tor ?i func?ia  main() ca s? v? convinge?i c?: max(3, 7, 5) ?ntoarce 7, max(3.2, 2.7, 5.3, 4.1) ?ntoarce 5.3, iar max("abdc", "abcd", "ardc", "abac") ?ntoarce "ardc". Explica?i ?n ce mod compilatorul va rezolva aceste apeluri.

Exerci?iul 2.2. Verifica?i faptul c? la func?ii generice se admite specificarea parametrilor implici?i.

Func?ia generic? poate s? depind? de mai multe tipuri parametrice. Forma de descriere de o asemenea func?ie este urm?toarea:

template <class tip_1[, class tip_2 ...]> tip_retur nume_func?ie (list?_parametri)

{

// Corpul func?iei

}

Pentru exemplu cre?m o func?ie generic? care afl? valoarea maxim? dintre elementele unui vector. Func?ia va depinde de dou? tipuri parametrizate. Unul va parametriza tipul elementelor vectorului dat, cel de al doilea tip va parametriza tipul indicilor.

<#include "iostream.h"
 
template <class T, class U> T max(T vt[], U n)
{
   T vmax=vt[0];
   for (U i=1; i<n; i++)
     if(vt[i]>vmax)
       vmax=vt[i];
   return vmax;
}
 
void main()
{
  double vd[5]={6.2, 9.5, 3.7, -5.2, 2.1};
  cout << "max din double = " << max<double, long>(vd, 5l) << endl;
//  ?ncerca?i ?i a?a
//  cout << "max din double = " << max(vd, 5l) << endl;
 
  int vi[5]={6, 9, 3, -5, 2};
  cout << "max din int = " << max<int, char>(vi,(char)5) << endl;
//  ?ncerca?i ?i a?a
//  cout << "max din int = " << max(vi, (char)5) << endl;
 
  char c; cin >> c;
}


Rezultatul afi?at va ar?ta astfel:

max din double = 9.5
max din int = 9

Exerci?iul 2.3. Examina?i de sine st?t?tor urm?toarea versiune a func?iei generice max():

template <class T=double, class U=int>
T max(T vt[], U n)
{
   T vmax=vt[0];
   for (U i=1; i<n; i++)
     if(vt[i]>vmax)
       vmax=vt[i];
   return vmax;
}


Re?ine?i c? func?ia generic? nu poate folosi o specificare de editare de leg?turi, utiliz?nd doar editoarele de leg?turi din C++.
_________________________
Autorul: dr.conf. S. Pereteatcu

2.8. Supra?nc?rcarea func?iilor

2.8. Supra?nc?rcarea func?iilor

?n limbajul C nu exist? posibilitatea de a crea mai multe func?ii cu unul ?i acela?i nume. Presupunem c? avem necesitate de o func?ie care afl? valoarea maximal? din dou? pentru diferite tipuri de date. ?n C trebuie s? invent?m c?te un nume de func?ie pentru fiecare tip de date ale parametrilor, cum ar fi, de exemplu, imax() pentru date de tipul int, fmax() pentru date de tipul float, dmax() pentru date de tipul double etc.

?n C++ dou? sau mai multe func?ii diferite pot avea unul ?i acela?i nume, ?ns? ele trebuie s? difere prin tipul ?i/sau num?rul de parametri.

Func?iile care au unul ?i acela?i nume ?i difer? ?ntre ele prin tipurile parametrilor ?i/sau num?rul de parametri se numesc func?ii supra?nc?rcate (engl. overload). Procesul de creare a func?iilor respective se nume?te supra?nc?rcare (engl. overloading).

Deoarece nimic nu a fost spus despre tipul rezultatului returnat, reiese c? func?iile supra?nc?rcate pot s? posede diferite tipuri ale rezultatului de returnare.

Urm?torul exemplu ilustreaz? modul de supra?nc?rcare a func?iilor:

#include "iostream.h"
 
int max(int i1, int i2) // prima func?ie max() afl? elementul maximal               
{                       // din dou? valori de tipul int    
   return i1>i2 ? i1 : i2;
}
 
double max(double d1, double d2) // a doua func?ie max() afl?                   
{                                // elementul maximal din dou? valori 
                                 // de tipul double
   return d1>d2 ? d1 : d2;
}
 
char* max(char* s1, char* s2) // afl? ?irul maximal din dou? 
{                            
   int i=0;
   while( (*(s1+i)!='\0') && (*(s1+i)==*(s2+i)) )
      i++;
   return *(s1+i)>*(s2+i)? s1 : s2;
}
 
void main()
{
   int i=2, k=10;
   cout << "max(" << i << ", " << k << ")="
        << max(i, k) << endl;
   cout << "max(" << 3.14 << ", " << 2.25 << ")="
        << max(3.14, 2.25) << endl;
   char *s1="abcd";
   char *s2="abd";
   cout << "s1=\"" << s1 << "\"" << endl;
   cout << "s2=\"" << s2 << "\"" << endl;
   cout << "max(s1, s2)=\"" << max(s1, s2) << "\""
        << endl;
   cout << "max(\"" << "abdc" << "\", \"" << "abcd"
        << "\")=\"" << max("abdc", "abcd") << "\""
        << endl;
 
   char c; cin >> c;
}


?n acest program mai ?nt?i a fost descris? func?ia max() care afl? ?i ?ntoarce valoarea maximal? din dou? valori date de tipul int. Apoi, aceasta func?ie a fost supra?nc?rcat? de dou? ori. O dat?  pentru aflarea valorii maximale din dou? valori de tipul double ?i a doua oar? pentru aflarea ?irului maximal din cele dou?. Algoritmul pentru ?iruri de caractere difer? esen?ial de cel pentru numere. De fapt, la realizarea lui putea fi folosit? ?i func?ia strcmp(), dar ea nu a fost utilizat? ?n acest exemplu, pentru a exclude necesitatea de a conecta fi?ierul antet "string.h".

?n func?ia main() se demonstreaz? apelurile func?iilor supra?nc?rcate. Programul afi?eaz? urm?torul text:

max(2, 10)=10
max(3.14, 2.25)=3.14
s1="abcd"
s2="abd"
max(s1, s2)="abd"
max("abdc", "abcd")="abdc"

Acest text arat? rezultatele de apelare a func?iilor supra?nc?rcate. Prelucr?nd apelurile func?iilor supra?nc?rcate (max(i, k), max(3.14, 2.25), max(s1, s2), max("abdc", "abcd")), compilatorul se orienteaz? dup? tipurile argumentelor ?i decide care din cele trei func?ii supra?nc?rcate trebuie s? fie apelat? ?n fiecare caz concret.

Nu este neap?rat ca func?iile supra?nc?rcate s? fac? unul ?i acela?i lucru. ?n principiu, ele pot s? difere, ?i s? difere esen?ial. De exemplu, putem supra?nc?rca func?ia max() pentru tipul double ?n a?a mod ca ea s? calculeze ?i s? ?ntoarc?, de exemplu, r?d?cina p?trat? din suma p?tratelor a dou? argumente. ?ns?, nu se recomand? de a face a?a ceva. Ar fi de dorit ca func?iile supra?nc?rcate cu unul ?i acela?i nume s? fie ?nrudite din punctul de vedere al sensului lor.

Cum a fost deja men?ionat, func?iile supra?nc?rcate trebuie s? difere prin tipul ?i/sau num?rul de parametri. Dac?, de exemplu, ?n programul precedent ad?ug?m urm?toarea supra?nc?rcare a func?iei max():

double max(int i1, int i2)
{
   return i1>i2 ? i1 : i2;
}


atunci programul devine gre?it, fiindc? la apelurile de tipul max(i, k) compilatorul nu va ?tie care din dou? func?ii int max(int i1, int i2) sau double max(int i1, int i2) trebuie s? fie apelat?. Compilatorul nu va admite a?a ceva ?i va afi?a un mesaj de eroare de tipul: "Earlier declaration of 'max(int,int)'". Programul nu va fi compilat cu succes, cu at?t mai mult el nu va fi rulat.

Limbajul C++, ca ?i limbajul C, admite conversii automate dintr-un tip ?n alt tip, mai ales la apelarea func?iilor. De aceea, la supra?nc?rcarea func?iilor apare problema de ambiguitate, c?nd pentru dou? sau mai multe func?ii supra?nc?rcate compilatorul nu poate s? stabileasc? care anume din aceste func?ii supra?nc?rcate trebuie s? fie apelat?. De exemplu, dac? ad?ugam ?n func?ia main() din exemplul precedent instruc?iunea:

cout << max(6.28, 5) << endl;

programul va deveni gre?it, fiindc? ?nt?lnind apelul max(6.28, 5) compilatorul nu va ?tie care din dou? func?ii int max(int i1, int i2) sau double max(double d1, double d2) trebuie s? fie apelat?. ?n a?a caz se spune c? aceast? instruc?iune este ambigu?, iar a?a situa?ie se nume?te ambigu? ?i este o eroare. Compilatorul ?n a?a caz va afi?a ceva de tipul: ”Ambiguity between 'max(int,int)' and 'max(double,double)'” ?i programul nu va fi compilat cu succes ?i, deci, nu va fi rulat. ?n acest caz trebuie de precizat anume care func?ie trebuie s? fie apelat?. Dac? vom scrie instruc?iunea a?a:

cout << max(6.28, (double)5) << endl;

atunci va fi apelat? func?ia double max(double d1, double d2) ?i va fi afi?at 6.28.

Dac? vom scrie instruc?iunea a?a:

cout << max((int)6.28, 5) << endl;

atunci va fi apelat? func?ia int max(int i1, int i2) ?i va fi afi?at 6.

Problema de ambiguitate poate s? apar? ?i din cauza transmiterii parametrilor prin referin??. Dac?, de exemplu, vom mai insera, printre func?iile supra?nc?rcate ?n programul de mai sus, ?i urm?toarea func?ie, care va fi compilat? cu succes:

int max(int &i1, int &i2)
{
   return i1>i2 ? i1 : i2;
}


atunci va fi ambiguu orice apel de tipul max(i, k), fiindc? compilatorul nu va ?ti care din cele dou? func?ii max() (cea cu transmiterea parametrilor prin valoare sau cea cu transmiterea parametrilor prin referin??) trebuie s? fie apelat?. ?n acest caz compilatorul va afi?a mesajul de eroare: ”Ambiguity between 'max(int,int)' and 'max(int&,int&)'”, ?n timp ce apelul max(8, 3) va fi corect, deoarece ?n cazul nostru constantele nu pot fi transmise prin referin?e; astfel, compilatorul va alege func?ia int max(int i1, int i2). Din aceste considerente, nu va fi ambiguu nici apelul max((const int)i, (const int)k), la care argumentele sunt convertite explicit la constante de tipul int.

Compilatorului ?i este suficient s? precizeze numai printr-un parametru func?ia care trebuie s? fie apelat?. Urm?toarele apeluri nu sunt ambigue ?n exemplul discutat: max((const)i,(const)k), max((const)i,k), max(i,(const)k), max(i,5), max(4,k). Situa?ia se va schimba, dac? vom ?nlocui func?ia int max(int &i1, int &i2) cu int max(const int &i1, const int &i2). Cercetarea  acestui caz r?m?ne ca exerci?iu.

?nc? un izvor de ambiguitate reprezint? folosirea parametrilor implici?i ?n func?iile supra?nc?rcate. De exemplu, nimic nu ne ?mpiedic? s? inser?m ?n programul de mai sus o supra?nc?rcare a func?iei max() care va afla ?i va ?ntoarce valoarea maximal? din trei valori de tipul int. Aceast? supra?nc?rcare poate ar?ta astfel:

int max(int i1, int i2, int i3)
{
   return i1>i2 ? (i1>i3 ? i1 : i3) : (i2>i3 ? i2 : i3);
}


?i dac? ?n func?ia main() mai inser?m instruc?iunea:

cout << "max(7, 3, 9)=" << max(7, 3, 9) << endl;

ea ne va afi?a:

max(7, 3, 9)=9

c?nd va fi ?ndeplinit? ?n func?ia main(). Adic?, aici totul este ?n regul?. ?ns?, dac? vom insera ?n programul de mai sus versiunea urm?toare a func?iei max() cu trei parametri, printre care cel pu?in unul va avea valoarea implicit?, ?n loc de versiunea precedent?:

int max(int i1, int i2, int i3=0)
{
   return i1>i2 ? (i1>i3 ? i1 : i3) : S(i2>i3 ? i2 : i3);
}


atunci programul devine gre?it, fiindc? orice apel de tipul max(i, k) devine ambiguu ?i provoac? din partea compilatorului mesajul de tipul: "Ambiguity between 'max(int,int)' and 'max(int,int,int)'". ?ntr-adev?r, compilatorul nu ?tie care dintre cele dou? func?ii max() trebuie s? fie apelat?: cea cu doi parametri sau cea cu trei av?nd valoarea implicit? 0 pentru parametrul trei.

Deci, supra?nc?rc?nd func?iile, programatorul trebuie permanent s? ?in? cont de posibilitatea provoc?rii ambiguit??ilor.

Supra?nc?rcarea func?iilor este unul dintre mecanismele care asigur? proprietatea de polimorfism ?n C++ ca limbaj de programare orientat spre obiecte.

Pe l?ng? no?iunea de supra?nc?rcare vom folosi ?i no?iunea de suprascriere a func?iilor.

 

Prin suprascrierea (engl. overriding) func?iei vom ?n?elege ?nlocuirea ?ntr-un domeniu de ac?iune a unei func?ii cu o alt? func?ie al c?rei antet acoper? antetul func?iei suprascrise. Func?ia suprascris? r?m?ne ?n existen?? ?i poate fi apelat? ?n acest domeniu prin precizare suplimentar?.

Un exemplu de suprascriere este suprascrierea unei func?ii membre mo?tenite ?ntr-o clas? derivat? (a se vedea tema „Suprascrierea ?i supra?nc?rcarea func?iilor membre ?n clasa derivat?”).
_________________________
Autorul: dr.conf. S. Pereteatcu

2.7. Func?ii inline

2.7. Func?ii inline

Dac? ?ntr-un program destul de des (de exemplu, ?n ciclu) se apeleaz? func?ii mici, aceasta poate conduce la pierderi suplimentare de timp. Timpul poate fi pierdut la organizarea salturilor spre func?ii ?i  la ?ntoarcerea ?napoi. O ie?ire din asemenea situa?ie poate fi indica?ia compilatorului c? ?n loc de apelul func?iei concrete s? se insereze codul func?iei. Acest proces se nume?te dezvoltarea codului ?n interior ?i este similar cu folosirea macrocomenzilor ?n limbajul de asamblare. A?a func?ii ?n C++ se numesc func?ii inline.

Func?ia inline este func?ia care se dezvolt? ?n interior ?n locul apelului. Func?ia ?n program devine inline prin prefixarea antetului ei la definire cu cuv?ntul-cheie inline.

Cercet?m exemplul:

#include "iostream.h"
inline int XOR(int a, int b) // antetul func?iei te tipul inline
{
   return (a||b) && !(a&&b);
}
 
void main()
{
   cout << "XOR(0, 0) = " << XOR(0, 0) << endl;
   cout << "XOR(0, 1) = " << XOR(0, 1) << endl;
   cout << "XOR(2, 0) = " << XOR(2, 0) << endl;
   cout << "XOR(2, 1) = " << XOR(2, 1) << endl;
   char c; cin >> c;
}


?n acest exemplu func?ia XOR() a fost declarat? ca func?ie inline. Pentru ca acest exemplu s? fie clar p?n? la urm?, vom explica ce face func?ia XOR(). ?n limbajul C este un set de operatori cu ac?iune pe bi?i, de exemplu, & (?I pe bi?i), | (SAU pe bi?i). ?n acest set intr? ?i operatorul ~ (XOR, adic? SAU exclusiv pe bi?i). Pentru operatorii & ?i | este ?i forma logic?, respectiv && ?i ||.

Pentru operatorul ~ a?a form? nu este prev?zut?, dar ea poate fi u?or realizat? prin func?ie. Tocmai aceasta ?i face func?ia XOR(), adic? ea ?ntoarce valoarea 0 (false) c?nd ambele argumente sunt zero sau ambele sunt diferite de zero, ?i ?ntoarce valoarea 1 (true) c?nd unul din argumente este zero ?i cel?lalt este diferit de zero. Dac? lans?m programul de mai sus, ob?inem urm?torul rezultat:

XOR(0, 0) = 0
XOR(0, 1) = 1
XOR(2, 0) = 1
XOR(2, 1) = 0

?n antetul func?iei putem scrie inline int sau int inline, ordinea nu conteaz?. ?n ambele cazuri, compilatorul prime?te indica?ie ca ?n orice loc unde se apeleaz? func?ia XOR() s? fie pus codul acestei func?ii, ceea ce m?re?te viteza de execu?ie. ?ns? utilizarea op?iunii inline conduce la m?rirea codului surs?, mai ales dac? func?iile inline sunt relativ mari ?i se apeleaz? ?n mai multe locuri.

Trebuie de subliniat faptul c? indica?ia de a executa func?ia inline nu este obligatorie pentru compilator, ea numai exprim? dorin?a programatorului. Dac? compilatorul concret poate s? dezvolte ?n memorie func?ia dat?, ea va fi dezvoltat? ca func?ie inline, dac? nu – atunci func?ia va fi apelat? ca o func?ie obi?nuit?. De obicei, nu pot fi expandate ca func?ii inline func?iile ce con?in m?car una din instruc?iunile for, while, do-while, switch. Dac? un compilator nu poate dezvolta o func?ie ?n memorie, el afi?eaz? un mesaj de avertizare, ceva de tipul: ”Functions containing for are not expanded inline”, ?i ignor? op?iunea inline. Deci, totul depinde de compilatorul concret.
_________________________
Autorul: dr.conf. S. Pereteatcu

2.6. Parametrii implici?i

2.6. Parametrii implici?i

?n C++ a ap?rut posibilitatea de a indica valorile implicite pentru parametri ?n descrierea func?iilor. Examin?m urm?torul program, care pentru un set de puncte ?n sistemul de coordonate cartezian bidimensional afi?eaz? informa?ii despre repartizarea punctelor pe cadrane:

#include "iostream.h"
 
void printcadran(double x, double y=0.0, const char *ln="\n")
{
   cout << "Punctul (" << x << "," << y 
        << ") se afla ";
   if(y>0.0)
      if(x>0.0)
         cout << "in cadranul I";
      else
         if(x<0.0)
            cout << "in cadranul II";
         else
            cout << "pe axa OY";
   else
      if(y<0.0)
         if(x<0.0)
            cout << "in cadranul III";
         else
            if(x>0.0)
               cout << "in cadranul IV";
            else
               cout << "pe axa OY";
      else
         if(x!=0.0)
            cout << "pe axa OX";
         else
            cout << "in originea";
 
   cout << ln; // ad?ugarea la mesajul principal 
}
 
void main()
{
   printcadran(1., 1., ". "); // f?r? valori implicite
   printcadran(-1., 1.); // implicit pentru parametrul 3
   printcadran(-1., -1., ". "); // f?r? valori implicite
   printcadran(1., -1.); // implicit pentru parametrul 3
   printcadran(0., 1., ". "); // f?r? valori implicite
   printcadran(1.); // implicit pentru parametrii 2 si 3
   double xt=-1.0,  yt=-1.0;
   printcadran(0., yt, ". "); // f?r? valori implicite
   printcadran(xt); // implicit pentru parametrii 2 si 3
   printcadran(0.); // implicit pentru parametrii 2 si 3
 
   char c; cin >> c;
}


Primul rol ?n acest program ?i revine func?iei printcadran() cu trei parametri. Primii doi reprezint? coordonatele punctului de testare (respectiv x ?i y). Prin al treilea parametru poate fi transmis un ?ir de caractere care se adaug? la mesajul afi?at despre apartenen?a punctului (x, y) unuia din cele patru cadrane sau uneia din cele dou? axe de coordonate.

Func?ia main() apeleaz? consecutiv func?ia printcadran() cu diferite valori pentru coordonate. Codul func?iei printcadran() este destul de transparent, a?a c? atragem aten?ia la descrierea antetului ei, fiindc? el con?ine valorile implicite pentru ultimii doi parametri. Modul obi?nuit de apelare a func?iei printcadran(), de exemplu, este:

printcadran(2.5, 3.1, "\n");

Rezultatul apelului va fi afi?area textului:

Punctul (2.5,3.1) se afla in cadranul I

?i cursorul va fi pozi?ionat la ?nceputul liniei urm?toare. ?ntr-adev?r, primele dou? argumente dau coordonatele punctului care se afl? ?n cadranul I (acestea sunt, respectiv, 2.5 ?i 3.1). Argumentul trei arat? ?irul care trebuie afi?at la coada mesajului principal. ?n cazul nostru "\n" ?nseamn? c? dup? afi?area mesajului principal cursorul va fi deplasat la ?nceputul r?ndului urm?tor, lucru care, consider?m, trebuie s? fie f?cut ?n majoritatea cazurilor dup? afi?area mesajului principal. De aceea, pentru ultimul parametru (pointer la un ?ir de caractere constant) a fost indicat? valoarea implicit? (const char *ln="\n"), adic? adresa ?irului "\n". Ceea ce ?nseamn? c? putem apela func?ia printcadran() d?nd numai dou? argumente, iar pentru argumentul trei va fi luat? valoarea implicit?. Adic? apelul:

printcadran(2.5, 3.1);

este echivalent cu apelul:

printcadran(2.5, 3.1, "\n");

Func?ia printcadran() are valoarea implicit? ?i pentru parametrul doi (double y=0.0). Aceasta ?nseamn? c? apelul:

printcadran(0.5);

este echivalent cu apelul:

printcadran(0.5, 0.0);

?i cu apelul:

printcadran(0.5, 0.0, "\n");

?n func?ia main() sunt date diferite exemple de apelare a func?iei printcadran(). Programul fiind rulat va afi?a urm?torul text:

Punctul (1,1) se afla in cadranul I. Punctul (-1,1) se afla in cadranul II

Punctul (-1,-1) se afla in cadranul III. Punctul (1,-1) se afla in cadranul IV

Punctul (0,1) se afla pe axa OY. Punctul (1,0) se afla pe axa OX

Punctul (0,-1) se afla pe axa OY. Punctul (-1,0) se afla pe axa OX

Punctul (0,0) se afla in originea

Caracterele din textul dat au fost special mic?orate ?n carte, pentru ca textul s? se ?ncadreze ?n limitele paginii, a?a cum el a fost aranjat pe ecran.

La crearea ?i apelarea func?iilor cu parametrii implici?i sunt anumite restric?ii.

?n primul r?nd, dac? vrem s? indic?m valoarea implicit? pentru un parametru care nu este ultimul ?n lista parametrilor, obligatoriu trebuie indicate valorile implicite ?i pentru to?i ceilal?i parametri care urmeaz? dup? el, altfel va fi o eroare. De exemplu, urm?toarele anteturi vor fi gre?ite ?i nu vor fi compilate:

//nu este indicat? valoarea implicit? pentru parametrul ln;

void printcadran(double x, double y=0.0, const char *ln);

//nu este indicat? valoarea implicit? pentru parametrul y;

void printcadran(double x=0.0, double y, const char *ln="\n") ;

//nu sunt indicate valorile implicite pentru parametrii y ?i ln;

void printcadran(double x=0.0, double y, const char *ln);

//nu este indicat? valoarea implicit? pentru parametrul ln;

void printcadran(double x=0.0, double y=0.0, const char *ln);
La apelul func?iilor cu parametri implici?i, dac? vrem s? omitem un argument pentru care este prev?zut? valoarea implicit?, vom fi nevoi?i s? omitem ?i toate celelate argumente care urmeaz? dup? el. A?adar, func?ia printcadran() poate fi apelat? cu valoarea implicit? pentru parametrul y numai dac? ?i pentru parametrul ln va fi utilizat? valoarea implicit?.

Adic?, apelul de tipul printcadran(2.5,," Eroare!") va fi gre?it, deoarece a fost omis argumentul doi ?i este prezent argumentul trei. Corect va fi numai printcadran(2.5), sau printcadran(2.5, 0.0," Corect!") dac? valoarea implicit? pentru parametrul ln ?n acest caz nu ne aranjeaz?.

?n majoritatea cazurilor, pentru indicarea valorilor implicite se folosesc constante. ?ns?, poate fi indicat? ?i o variabil?, sau chiar o expresie, cu condi?ia c? toate variabilele ce se con?in ?n aceast? expresie sunt cunoscute la momentul c?nd se indic? valoarea explicit?. De exemplu, dac? declar?m func?ia printcadran() astfel:

double z=5.2;
char *s;
void printcadran(double x, double y=(z+1.0)*3.0,
                 const char *ln=s)
{}


atunci valoarea implicit? pentru parametrul ln va fi valoarea curent? (la momentul apelului) a pointerului s, iar valoarea implicit? pentru parametrul y va fi rezultatul expresiei (z+1.0)*3.0 calculat? ?n momentul apelului func?iei printcadran().

P?n? acum a fost cercetat? specificarea valorilor implicite pentru argumente care se transmit prin valoare. Este posibil? specificarea valorilor implicite ?i pentru argumentele care se transmit prin referin?e. ?n cazul acesta, valoarea care se indic? ca valoare implicit? nu poate fi o constant?, ci va fi luat? dintr-o variabil? specificat?, care este cunoscut? la momentul descrierii antetului func?iei. ?n programul de mai sus prefacem antetul func?iei printcadran() astfel:

double  z;
void printcadran(double &x=z, double &y=z, const char *ln="\n")
{}
Iar func?ia main() o prefacem astfel:
void main()
{
   double x1=1.0, x2=-1.0, x3=0.0, y1=1.0;
   z=1.0;
   printcadran(x1, y1, ". "); // f?r? valori implicite
   printcadran(x2); // implicit pentru parametrii 2 ?i 3
   z=-1.0;
   printcadran(x2, z, ". "); // f?r? valori implicite
   printcadran(x1); // implicit pentru parametrii 2 ?i 3
   printcadran(x3, y1, ". "); // f?r? valori implicite
   printcadran(x1, x3); // implicit pentru parametrul 3
   printcadran(x3, x2, ". "); // f?r? valori implicite
   z=0.0;
   printcadran(x2); // implicit pentru parametrii 2 ?i 3
   printcadran(); // implicit pentru toate argumentele
 
   char c; cin >> c;
}


Fiind rulat, programul modificat va afi?a exact acela?i text ca ?i programul ini?ial. ?n versiunea modificat? a func?iei printcadran() parametrii x ?i y se transmit prin referin?? ?i pentru ambii a fost specificat? valoarea implicit?. A?a este ?i ?n exemplul respectiv, ?ns? nu este obligatorie nici una, nici alta.

Adic?, modul de transmitere poate fi diferit pentru diferi?i parametri. La specificarea valorilor implicite r?m?ne regula „parametrului din dreapta”, adic? specificarea valorii implicite pentru parametrul x era posibil? fiindc? a fost specificat? valoarea implicit? pentru y, iar specificarea valorii implicite pentru y era posibil? fiindc? a fost specificat? valoarea implicit? pentru ln. ?n acest exemplu valorile implicite pentru parametrii x ?i y vor fi luate din una ?i aceea?i (nu este obligatoriu) variabil? z ?i, ca urmare, la apelul func?iei printcadran() f?r? argumente va fi testat punctul cu coordonatele egale.

La aceast? tem? mai avem de discutat despre locurile unde pot fi specificate valorile implicite pentru parametri. ?n exemplele precedente func?ia printcadran() a fost definit? complet ?nainte de a fi utilizat?. Cercet?m cazul c?nd ?n partea de sus a programului va fi declarat numai prototipul func?iei printcadran(), iar defini?ia func?iei va fi pus? ?n partea de jos a programului, conform schemei:

<#include "iostream.h"
 
void printcadran(double x, double y, const char *ln);
 
void main()
{}
void printcadran(double x, double y, const char *ln)
{}


S? presupunem c? vrem s? specific?m valorile implicite 0.0 pentru x ?i y ?i valoarea "\n" pentru ln. Cel mai potrivit loc pentru aceasta este declararea func?iei printcadran() din partea de sus a programului, adic? s? scriem:

void printcadran(double x=0.0, double y=0.0,const char *ln="\n");

sau chiar f?r? numele parametrilor (numai cu spa?iile ?nainte de caracterul '='):

void printcadran(double =0.0, double =0.0,const char* ="\n");

Se admite specificarea valorilor implicite pentru parametri ?i la definirea func?iei, adic? l?s?m prototipul func?iei f?r? valorile implicite ale parametrilor ?i vom scrie definirea func?iei astfel:

void printcadran(double x=0.0, double y=0.0, const char *ln="\n")
{}


?n a?a caz, utilizarea argumentelor implicite se admite numai ?n func?iile a c?ror definire urmeaz? dup? definirea func?iei printcadran(). ?n ultimul exemplu nu putem scrie ?n func?ia main() apelurile cu argumentele implicite (de tipul printcadran(2.3, -1.0);,  printcadran(2.3);,   printcadran();), fiindc? ?n func?ia main() ?nc? nu este ?tiut c? func?ia printcadran() are argumente implicite. ?ns?, putem proceda astfel:

<#include "iostream.h"
 
void printcadran(double x, double y, const char *ln);
void f();
 
void main()
{
   …
   printcadran(1.0, 1.0, ”\n”); // apelul f?r? argumente implicite   
   …
   f(); // apelul func?iei f() ?n care se apeleaz? func?ia cu
        // argumente implicite}
void printcadran(double x=0.0, double y=0.0, const char *ln="\n")
{}
void f()
{
   printcadran(2.3, -1.0);
   printcadran(2.3);
   printcadran();
}


?n func?ia f() apelurile func?iei printcadran() cu argumente implicite sunt corecte.

?n general, o parte de valori implicite poate fi specificat? la declararea func?iei ?i o parte – la definirea func?iei. ?n orice caz, respect?m urm?toarele reguli:

  • specificarea valorii implicite pentru fiecare parametru poate fi f?cut? numai ?ntr-un singur loc;
  • valoarea implicit? pentru un parametru poate fi specificat? dac? sunt specificate valorile implicite pentru to?i parametrii ce urmeaz? dup? el;
  • argumentele implicite pot fi utilizate ?n locurile unde valorile implicite pentru parametrii respectivi sunt cunoscute;
  • parametrul ce are valoarea implicit? poate fi omis ?n apelul func?iei, dac? sunt omi?i ?i to?i parametrii din partea dreapta a lui.

_________________________
Autorul: dr.conf. S. Pereteatcu

2.5. Transmiterea parametrilor prin referin?e

2.5. Transmiterea parametrilor prin referin?e

?n limbajul C exist? doar o singur? modalitate de transmitere a parametrilor ?n func?ii: prin valoare (copie). Astfel, dac? vrem ca o func?ie s? modifice valoarea variabilelor care se transmit ca parametri, trebuie transmise func?iei adresele variabilelor respective.
De exemplu, avem nevoie de o func?ie care schimb? reciproc valorile ?ntre dou? variabile. A?a func?ie poate fi de mare folos la sortarea elementelor unui vector. Versiunea func?iei reprezentat? ?n urm?torul program este gre?it?.

#include "iostream.h"
 
void schimba_reciproc(double a, double b)
// parametrii vor fi transmi?i prin valoare
{
   double temp = a;
   a = b;
   b = temp;
}
 
void main()
{
   double x=2.5, y=3.14;
   cout << "x=" << x << " y=" << y << endl;
   schimba_reciproc(x, y);
   cout << "x=" << x << " y=" << y << endl;
 
   char c; cin >> c; 
}


Fiindc? la apelul func?iei schimba_reciproc(x, y) vor fi transmise copiile valorilor variabilelor x ?i y, valorile variabilelor x ?i y ?n func?ia main() vor r?m?ne neschimbate. Aceasta confirm? ?i rezultatul programului:

x=2.5 y=3.14
x=2.5 y=3.14


Observ?m c? valorile variabilelor x ?i y au r?mas acelea?i. ?n limbajul C exist? o solu?ie unic? de a rezolva aceast? problem?. Trebuie de transmis func?iei adresele variabilelor ?i nu valorile lor. Modific?m programul precedent, ?ntruc?t el va ar?ta astfel:

#include "iostream.h"
 
void schimba_reciproc(double *a, double *b)
// se transmit dou? adrese (doi pointeri)
{
   double temp = *a;
   *a = *b;
   *b = temp;
}
void main()
{
   double x=2.5, y=3.14;
   cout << "x=" << x << " y=" << y << endl;
   schimba_reciproc(&x, &y); // transmitem adresele
                             // variabilelor x ?i y
   cout << "x=" << x << " y=" << y << endl;
 
   char c; cin >> c; 
}


Func?ia schimba_reciproc() a fost modificat? esen?ial. Acum ea prime?te ?n calitate de argumente doi pointeri cu adresele variabilelor de tipul double. Prin intermediul acestor pointeri func?ia dat? are acces la variabilele x, y din func?ia main(). Atragem aten?ia c? s-a schimbat ?i modul de apelare a func?iei schimba_reciproc(). Lans?m programul ?i ob?inem:

x=2.5 y=3.14
x=3.14 y=2.5


Acum rezultatele sunt corecte. Totu?i, ultima versiune a func?iei are un neajuns. De fiecare dat? c?nd programatorul scrie ?n program apelul func?iei schimba_reciproc(), el trebuie s? ?in? cont c? se indic? adresele variabilelor ?i nu ?nse?i variabilele. Adic?, dac? ar fi posibil apelul:

schimba_reciproc(x, y);

el va arat? mai simplu, mai laconic ?i mai natural dec?t apelul:

schimba_reciproc(&x, &y);

?n limbajul C++ exist? posibilitatea de a transmite parametrii ?n func?ii prin referin??.

Pentru ca prima versiune a programului s? fie corect?, modific?m ?n ea doar antetul la descrierea func?iei schimba_reciproc() astfel:

void schimba_reciproc(double &a, double &b)

// parametrii se transmit prin referin?e

Restul r?m?ne acela?i, inclusiv modul de apelare a func?iei schimba_reciproc(). Lans?m programul ?i ne convingem c? rezultatul va fi corect, adic? coincide cu cel de la versiunea cu pointeri.

Transmiterea parametrului prin referin?? (cum ar fi double &a) ?nseamn? c? func?ia respectiv? va lucra direct cu variabila care va fi pus? ca argument ?n apelul acestei func?ii.

Observ?m c? ?ntr-o func?ie se admite combinarea arbitrar? a modurilor de transmitere pentru parametri diferi?i. Programatorul el ?nsu?i urm?re?te acest lucru. De exemplu:

double suma(double *a, int n, int &k)
{}


Func?ia suma() are trei parametri. Primii doi se transmit prin valoare ?i ultimul prin referin?e.
_________________________
Autorul: dr.conf. S. Pereteatcu

Translate Переводчик

Подписка на новости

SmartResponder.ru
Ваш e-mail: *
Ваше имя: *

Хостинг для Wordpress сайтов