Friend vs. membri

Problema care se pune aici este cand preferam (daca nu cumva suntem chiar obligati) sa definim o functie ca membru si cand ca globala ?
Teoria generala privitoare la clase & obiecte zice ca interiorul unui obiect este format din:

De aici concluzionam ca functiile din categoriile setState, respectiv getAttrib ar fi mai normal sa le definim ca membri (de exemplu, la problema cu stiva operatiile push si pop sunt din catgoria
setState, iar top - din categoria getAttrib).
De altfel, recomandarile care apar in literatura sustin ca, atunci cand nu exista motive obiective, se va opta pentru membri. Aici trebuie spus ca un avantaj al functiilor membru este acela ca, in interiorul lor referirile la variabilele membru se fac direct, deci se lucreaza cu nume mai scurte, spre deosebire de functiile globale care sunt fortate sa foloseasca referintele explicite, de forma nume_obiect.nume_membru. Ca urmare, codul functiilor membru prezinta o claritate mai mare.

Acum sa vedem care ar fi acele "motive obiective" amintite mai sus, care ne-ar indemna sa recurgem la functii globale:
  • In primul rand este vorba de functiile care implementeaza operatii intre obiecte ale unor clase diferite, sau intre obiecte ale unei clase si valori apartinand unor tipuri primare. Aici sunt vizate de fapt functiile-operator. Acest caz este prezentat pe larg in Lucrarea 4 - Supraincarcarea Functiilor si a Operatorilor.
  • Presupunem ca avem 2 clase: Vector si Matrice; daca dorim sa implementam operatia de

  • inmultire a 2 matrici astfel incat sa acoperim si cazul particular al inmultirii intre un vector si o matrice, alegand varianta cu functii membru, ar trebui sa definim in ambele clase cate o functie corespunzatoare; alegand varianta cu functie globala, putem avea o singura functie de inmultire, si anume:
    Prevazand in clasa Matrice un constructor cu parametru de tip Vector&, vom putea efectua operatia de inmultire pentru toate combinatiile de operanzi. Din motive de optimizare vom da functiei statut de friend in clasa Matrice, astfel incat sa se evite accesul la datele obiectelor m1 si m2 via apeluri de genul getElem(i,j).
     
  • Tot in lista "motivelor obiective" de care aminteam la inceput, ar fi de mentionat si unul (care de fapt e mai putin obiectiv) legat de preferinta programatorilor pentru o notatie sau alta.

  • Considerand tot clasa Matrice, presupunem ca vrem sa definim o operatie de determinare a inversei unei matrici. Daca denumim aceasta operatie inversa, avem urmatoarele alternative:
      -ca functie membru, apelul ei s-ar face probabil la modul m.inversa(), aceasta notatie sugerand ca in urma executiei obiectul m va contine inversa valorii sale anterioare (desi nu e obligatoriu sa se intample asa);

      -ca functie globala, apelul ar fi inversa(m), aceasta notatie sugerand ca m nu se modifica, ci functia creaza un nou obiect care sa contina inversa lui m.
    In acest caz, alegerea poate fi dictata de preferintele pentru una sau alta dintre notatii.
     
  • In fine, exista situatii in care utilizarea functiilor friend permite exprimarea unor operatii intr-o notatie mai naturala. Consideram ca avem o clasa Rational care modeleaza lucrul cu numere rationale si presupunem ca este necesara o functie care sa returneze maximul dintre 2 numere rationale. Daca q1 si q2 sunt obiectele pentru care se aplica functia, este evident ca apelul q1.Maxim(q2), corespunzator variantei in care Maxim este functie membru, arata cam "contra naturii" fata de Maxim(q1,q2).

Hosted by www.Geocities.ws

1