[14] Przyjaciele klas (friend)
(Część C++ FAQ Lite, Copyright © 1991-2002, Marshall Cline, cline@parashift.com)


FAQ - sekcja [14]:


[14.1] Co to jest "przyjaciel klasy" ("friend")?

Jest to coś, co pozwala Twojej klasie uzyskać pełny dostęp do innej klasy lub funkcji.

Przyjacielami klasy mogą być zarówno funkcje jak i całe klasy. Przyjaciele klasy posiadają pełne prawa dostępu do jej składowych. Zazwyczaj programista ma polityczną i techniczną kontrolę zarówno nad przyjaciółmi jak i metodami klasy (w przeciwnym wypadku zapewne będziesz musiał(a) poprosić o zgodę właściciela innych fragmentów kodu, gdybyś chciał(a) uaktualnić Twoją klasę).

GóraDółPoprzednia sekcjaNastępna sekcjaSzukaj w FAQ ]


[14.2] Czy przyjaciele klasy naruszają enkapsulację?

Wręcz przeciwnie! Właściwie zastosowani, nawet wspierają enkapsulację.

Często pojawia się konieczność podzielenia istniejącej klasy na dwie części, z których każda będzie miała różną ilość obiektów lub różne czasy trwania (ang. "lifetime"). Zazwyczaj w takich przypadkach, obie połowy potrzebują mieć pełny dostęp do siebie nawzajem (obie części były wcześniej jedną klasą, więc nie spowoduje to zwiększenia ilości kodu, który wymaga pełnego dostępu do struktury danych; będzie to po prostu rozdzielenie tego samego kodu na dwie klasy zamiast jednej). Najbezpieczniej można to zrobić deklarując obie klasy jako swoich przyjaciół.

Jeżeli użyjesz "przyjaźni między klasami" podobnie jak w powyższym przykładzie, pozostawisz prywatne składowe prywatnymi. Ludzie którzy tego nie rozumią często podejmują marne wysiłki mające na celu uniknięcie stosowania "przyjaciół" w sytuacjach takich jak powyżej, przez co często niszczą oni ideę enkapsulacji. Albo zmieniają poziom dostępu do składowych na publiczny (groteska!), albo umożliwiają obu klasom wzajemny dostęp do składowych za pomocą publicznych metod typu get() i set(). Dostęp do prywatnych danych przez publiczne metody get() i set() jest w porządku, ale tylko gdy te prywatne dane mają jakieś zastosowanie na zewnątrz klasy (z punktu widzenia jej użytkownika). W wielu przypadkach tego typu pomocnicze metody są równie złe jak "upublicznianie" danych: ukrywają one jedynie nazwę prywatnej składowej, ale nie ją samą.

Analogicznie, jeśli użyjesz "przyjaźni" jako składniowej alternatywy dla publicznych metod get()/set(), nie naruszysz enkapsulacji bardziej, niż mógłbyś (mogłabyś) to uczynić przy użyciu metod. Innymi słowy, "przyjaciele klasy" nie naruszają barier enkapsulacji; wraz z metodami klasy stanowią one barierę enkapsulacji.

(Wielu ludzi uważa, że "przyjaciel klasy" jest czymś istniejącym na zewnątrz tej klasy. Spróbuj traktować "przyjaciół" jako fragment publicznego interfejsu klasy. "Funkcja-przyjaciel" wewnątrz deklaracji klasy nie narusza enkapsulacji w żaden inny sposób niż mogłaby to zrobić zwykła metoda: zarówno "funkcja-przyjaciel" jak i "zwykła metoda" mają dokładnie te same prawa dostępu do niepublicznych składowych klasy.)

GóraDółPoprzednia sekcjaNastępna sekcjaSzukaj w FAQ ]


[14.3] Jakie są zalety i wady stosowania "funkcji-przyjaciół"? UPDATED!

[Recently Wersja polska - poprawiono komentarz w kodzie: z "unieważnia" na "przeciąża" (in 9/02). Click here to go to the next FAQ in the "chain" of recent changes.]

Dają one pewien stopień swobody przy projektowaniu interfejsu.

Metody i "funkcje-przyjaciele" są jednakowo uprzywilejowane (całkowity dostęp). Główna różnica między nimi jest taka, że "funkcję-przyjaciela" wywołuje się tak: f(x), a metodę tak: x.f(). Stąd możliwość wyboru między zastosowaniem metody (x.f()) a "funkcji-przyjaciela" (f(x)) pozwala projektantowi wybrać składnię, która będzie bardziej czytelna w danym przypadku, co upraszcza zarządzanie kodem i obniża koszty.

Główną wadą "funkcji-przyjaciół" [w porównaniu do metod - przyp. tłum.] jest to, że jeśli chcesz, aby przy ich wywołaniu następowało dynamiczne wiązanie [wybieranie konkretnej wersji funkcji w zależności od typu obiektu; operacja ta następuje "automatycznie" w przypadku metod wirtualnych - przyp. tłum.], będziesz musiał(a) dopisać kilka dodatkowych linii kodu. W celu utworzenia "wirtualnej funkcji-przyjaciela", konieczne jest utworzenie ukrytej (najczęściej chronionej - protected) metody wirtualnej, która byłaby wywoływana przez "przyjaciela". Technika ta nazywana jest Virtual Friend Function Idiom. Na przykład:

 class Base {
 public:
   friend void f(Base& b);
   
// ...
 protected:
   virtual void do_f();
   
// ...
 };
 
 inline void f(Base& b)
 {
   b.do_f();
 }
 
 class Derived : public Base {
 public:
   
// ...
 protected:
   virtual void do_f();  
// "Przeciąża" działanie f(Base& b)
   
// ...
 };
 
 void userCode(Base& b)
 {
   f(b);
 }

Instrukcja f(b) w funkcji userCode(Base&) wywoła metodę b.do_f(), która jest metodą wirtualną. Oznacza to, że faktycznie nastąpi wywołanie metody Derived::do_f(), jeśli obiekt b należy do klasy Derived. Zwróć uwagę, że klasa Derived przeciąża chronioną wirtualną metodę do_f(); nie przeciąża natomiast "funkcji-przyjaciela" f(Base&).

GóraDółPoprzednia sekcjaNastępna sekcjaSzukaj w FAQ ]


[14.4] Co znaczy, że "przyjaźń nie jest dziedziczona, przechodnia lub obustronna"?

To, że gwarantuję Ci "przyjacielski dostęp do mnie" nie oznacza, że dostęp do mnie automatycznie dostają Twoje dzieci lub Twoi przyjaciele, ani że ja otrzymuję "przyjacielski dostęp" do Ciebie.

GóraDółPoprzednia sekcjaNastępna sekcjaSzukaj w FAQ ]


[14.5] Czy moja klasa powinna używać metod czy raczej "funkcji-przyjaciół"?

Używaj metod jeśli to możliwe, a "funkcji-przyjaciół" jeśli to konieczne.

Czasami "funkcje-przyjaciele" są lepsze pod względem składniowym (np. w klasie Fred, dla "funkcji-przyjaciół" nazwa Fred występuje po nazwie funkcji, a dla metod przed nazwą funkcji). Innym dobrym przykładem użycia "funkcji przyjaciół" są dwuargumentowe operatory arytmetyczne. Np. operacja aComplex + aComplex powinna być raczej zdefiniowana jako "funkcja-przyjaciel" niż metoda, jeśli chcesz jednocześnie umożliwić wykonywanie operacji aFloat + aComplex (metody nie pozwalają na rzutowanie ich "lewego" argumentu, gdyż zmieniałoby to klasę obiektu, do którego należy metoda).

W pozostałych przypadkach, wybieraj raczej metody niż "zaprzyjaźnione funkcje".

GóraDółPoprzednia sekcjaNastępna sekcjaSzukaj w FAQ ]


E-Mail E-mail the author
C++ FAQ LiteSpis treściSkorowidzO autorze©Pobierz swoją własną kopię ]
Ostatnia aktualizacja Jun 17, 2002
Wersja polska: 0.1i Jul 13, 2004