przyjaźń funkcji

0

Cześć :)
Założmy, że z jakąś klasą zaprzyjaźniamy funkcję/klasę.
Czy ma znaczenie, czy zrobimy to w sekcji prywatnej czy też publicznej? Jeżeli tak, to jakie?

1

Nie ma znaczenia.
Funkcja, której deklarujemy znajomość, nie jest częścią klasy.

0

Nie ma znaczenia, znowu pytasz o coś co w kilka sekund sprawdzisz sam.

0

Nie ma bo:

  • przyjaźni się nie dziedziczy
  • przyjaźń jest relacją pomiędzy klasą a przyjacielem (klasą lub funkcją), co nie ma wpływu na inny kod.
1

Trochę offtop ale czy ktoś z Was naprawdę używa przyjaźni? Jeśli tak, to czy moglibyście rozwinąć dlaczego?

1

Unikam zaprzyjaźnienia o ile to możliwe.

0

Co do offtopu:
A co jeśli klasa A ma pole typu klasy B i używa jej metod, a użycie metod klasy B poza ciałem klasy A nie ma sensu? Czy wtedy zabezpieczenie metod klasy B poprzez umieszczenie ich w sekcji prywatnej oraz zaprzyjaźnienie klasy B z A jest złe?

2
Aux napisał(a):

Co do offtopu:
A co jeśli klasa A ma pole typu klasy B i używa jej metod, a użycie metod klasy B poza ciałem klasy A nie ma sensu? Czy wtedy zabezpieczenie metod klasy B poprzez umieszczenie ich w sekcji prywatnej oraz zaprzyjaźnienie klasy B z A jest złe?

Imho tak, bo istnieje dużo prostsze i bardziej eleganckie rozwiązanie - zagnieżdżenie definicji klasy w obrębie części prywatnej klasy zewnętrznej.

 
class A {
public:
  void bar() { b.x = 5; b.foo(); }

private:
  struct B {
    int x;
    void foo() {}
  };
  B b;
};

int main() {
  //A::B b; to nie przejdzie
  return 0;
}
0
satirev napisał(a):
Aux napisał(a):

Co do offtopu:
A co jeśli klasa A ma pole typu klasy B i używa jej metod, a użycie metod klasy B poza ciałem klasy A nie ma sensu? Czy wtedy zabezpieczenie metod klasy B poprzez umieszczenie ich w sekcji prywatnej oraz zaprzyjaźnienie klasy B z A jest złe?

Imho tak, bo istnieje dużo prostsze i bardziej eleganckie rozwiązanie - zagnieżdżenie definicji klasy w obrębie części prywatnej klasy zewnętrznej.

 
class A {
public:
  void bar() { b.x = 5; b.foo(); }

private:
  struct B {
    int x;
    void foo() {}
  };
  B b;
};

int main() {
  //A::B b; to nie przejdzie
  return 0;
}

A co jeśli chcę, aby można było utworzyć obiekt klasy B na zewnątrz klasy A i użyć jej kilku metod przeznaczonych do użycia na zewnątrz klasy A, a dopiero potem podpiąć go pod klasę A?

0
Aux napisał(a):

Co do offtopu:
A co jeśli klasa A ma pole typu klasy B i używa jej metod, a użycie metod klasy B poza ciałem klasy A nie ma sensu? Czy wtedy zabezpieczenie metod klasy B poprzez umieszczenie ich w sekcji prywatnej oraz zaprzyjaźnienie klasy B z A jest złe?

Aux napisał(a):

A co jeśli chcę, aby można było utworzyć obiekt klasy B na zewnątrz klasy A i użyć jej kilku metod przeznaczonych do użycia na zewnątrz klasy A, a dopiero potem podpiąć go pod klasę A?

To w końcu jakie jest pytanie? Co ma być publiczne, a co chronione? Jak zadasz pytanie w sposób jednoznaczny to można będzie spróbować na nie odpowiedzieć...

0
satirev napisał(a):

Trochę offtop ale czy ktoś z Was naprawdę używa przyjaźni? Jeśli tak, to czy moglibyście rozwinąć dlaczego?

Zazwyczaj przeciążanie operatorów +-*/ << >> ładnie się łączy z przyjaźnią. Nie ma potrzeby wtedy tworzyć getterów tylko na potrzeby takich funkcji.
Poza tym w opisie klasy widać jakie operatory klasa obsługuje.

0

To było drugie pytanie.
Przykładowo:
Jest klasa Renderer, która ma pole typu IShader*. Tworzę obiekt klasy implementującej IShader i podpinam go pod klasę Renderer za pomocą settera(rzecz jasna na zewnątrz klasy Renderer). Nikt, poza klasą Renderer, nie może korzystać z metod przeznaczonych do rysowania należących do klasy IShader. Nie jest to wcale nierealna sytuacja, bo mam coś podobnego przed oczami. Może klasa jest źle zaprojektowana, nie wiem, dlatego pytam.

0

Ja kiedyś używałem regularnie przyjaźni, ale ukrytej pod makrami, w związku z używaniem d-pointer'ów (biblioteka do Qt, d-pointer'y pomagały w utrzymywaniu binary compatibility).

2
kobylecki napisał(a):

Zazwyczaj przeciążanie operatorów +-*/ << >> ładnie się łączy z przyjaźnią. Nie ma potrzeby wtedy tworzyć getterów tylko na potrzeby takich funkcji.
Poza tym w opisie klasy widać jakie operatory klasa obsługuje.

A po kiego ci gettery?

class Foo
  {
   private:
   int x;
   public:
   Foo(int x):x(x) {}
   ostream &prn(ostream &s)const { return s<<x; }
  };
inline ostream &operator<<(ostream &s,const Foo &f) { return f.prn(s); }
3

Nikt, poza klasą Renderer, nie może korzystać z metod przeznaczonych do rysowania należących do klasy IShader

Jesli zmienia ci sie publiczny interfejs w zaleznosci od miejsca jego uzycia, to znaczy, ze masz cos ostro nie tak z projektem, a nie, ze trzeba smieszne konstrukcje jezykowe wprowadzac.

1
_13th_Dragon napisał(a):

A po kiego ci gettery?

class Foo
  {
   private:
   int x;
   public:
   Foo(int x):x(x) {}
   ostream &prn(ostream &s)const { return s<<x; }
  };
inline ostream &operator<<(ostream &s,const Foo &f) { return f.prn(s); }

Chyba właśnie jeden zdefiniowałeś co się nazywa prn, co nie?

Ta metoda, nawet jak nie zwraca stan obiektu per se, który jest uważany przez nas za prywatny, to nadal daje możliwość użycia tego kawałka kodu nie tylko tam gdzie chcemy. I teraz używać prn może każdy (może zamockować stream i przechwycić stan x), a gdyby przeciążenie operatora było zaprzyjaźnione to wtedy nikt inny do tego stanu prywatnego by się nie dostał.

0

Po pierwsze, to nie jest getter, bo to jest: int X()const { return x; } chyba różnicę widzisz.
Po drugie, to że tą metodę można użyć do nieco innych celów jest plusem, nie minusem.

0

Widzę różnicę, nawet to napisałem, że to nie jest getter per se. Ale dalej prn jest publiczny i może odkrywać stan nie tylko dla operatora < <.
Nie wiem, czy ty widzisz zaletę przyjaźni w tym wypadku.

Inaczej, używając twoich słów: po kiego ci pisać definicje dwóch funkcji (prn i przeciążenie operatora < <) jak można tylko przeciążyć operator? :)

Pytanie oryginalne było o użycie przyjaźni i to użycie, które przedstawiłem jest jak najbardziej uzasadnione.

0

Z tym "odrywaniem stanu" to totalna bzdura, żadnego stanu tu nie odkrywasz, ale jeżeli wg ciebie prn odkrywa jakiś tam "stan" to operator << napisany za pomocą friend'a będzie dokładnie ten sam "stan" odkrywać.
Z friend dokładnie tyle samo trzeba napisać: deklaracja friend oraz sama funkcja. Nie istnieje żadnej zalety przyjaźni, prawie zawsze można to obejść.

3
_13th_Dragon napisał(a):

Po pierwsze, to nie jest getter, bo to jest: int X()const { return x; } chyba różnicę widzisz.
Po drugie, to że tą metodę można użyć do nieco innych celów jest plusem, nie minusem. [...] Z tym "odrywaniem stanu" to totalna bzdura, żadnego stanu tu nie odkrywasz,

Offtop z getterem ;) _13th_Dragon... kobylecki zasadniczo ma rację, więc wyluzuj ;] Bo tak to jakoś napisałeś, że zgubiłeś sens. Getter to nie jest pojęcie związane z C++, albo innym konkretnym językiem. Zresztą facet napisał to w cudzysłowie. Pojęcie ma się nijak do tego, jak zadeklarujemy funkcję, bo o semantyce mówi. Są różne konwencje zwracania wyniku. Getterem jest coś, czym możesz pobrać wartość.

int X() const { return x; } // to jest getter

int getX() const { return x; } // to jest getter chyba też

void getX(int* output) { *output = x; } // a to już nie jest getter, bo nie robi "return" ?? ;]

int setX() { return x; } // i może to też nie jest, bo nazwa zmieniona ?? ;]

// wersja combo, dziwna nazwa, dziwna konwencja zwracania wyniku

struct Response { int value; }

void query(Response& out) { out.value = x; } // hm ??

Przecież wszystkie te funkcje robią to samo. Z punktu widzenia analizy przepływu, to są gettery. Z punktu widzenia C++, to wszystko są gettery obfuskowane, bo nie trzymają się jakiejś-tam-konwencji zapisu. Ale to nie zmienia sensu ich działania ;)

1

@Ranides,
Zgadzam się że wszystko co wymieniłeś spokojnie można nazwać getterami nawet bez cudzysłowów mimo że nie wszystkie są konwencjonalne.
Natomiast @kobylecki widzi gettera w tym ostream &prn(ostream &s)const { return s<<x; } i ta metoda ponieważ jest publiczna odkrywa jakiś tam "stan" więc jest getterem.
A jeżeli ktoś czegoś nie zrozumiał to na 100% to ani ja ani @kobylecki
Poza tym to nie jest offtop, ponieważ od wyjaśnienia tego czy taka metoda coś niepotrzebnie odkrywa zależy wniosek czy w tym przypadku jest wskazana przyjaźń.

0

No właśnie też widzę ujawnienie stanu - dla mnie ten operator<<< jest dokładnie tym samym, co funkcja query, którą w przykładzie dałem ;) Składowa prywatna jest przekazywana bóg wie komu (jakiemuś dziadowi, co się jako strumień przedstawia). Mam wrażenie offtopa, bo operator<<< jest cholernie specyficzny, to jest obsługa serializacji. A różne są poglądy na temat tego, czy serializowanie powinno grzebać w prywatnych składowych i czy w ten sposób łamię enkapsulację albo ujawnia stan wewnętrzny.

Może bezpieczniej przeskoczcie na inny operator? W końcu przyjaźni trzeba w przypadku każdego operatora, który chce prywatnego dostępu, a nie może być metodą składową. Takich operatorów jest od licha. Jakieś operatory Number operator+(int, const Number&), -, *, /, ^, &, ...

Wszystkie arytmetyczne chyba będą mniej kontrowersyjne. Żeby klasa robiła wrażenie w "pełni arytmetycznej", to tych operatorów jest od groma. Masz rację @_13th_Dragon, że nigdy nie ma konieczności ich zaprzyjaźniać - delegować można zawsze. operator+ do publicznej metody add. operator- do publicznej metody sub. I tak dalej, i tak dalej... Ale tego jest cała kupa. No i ludzie niekoniecznie chcą mieć te wszystkie "opisowe" metody publiczne - skoro bawią się w przeładowywanie operatorów . No i przyjaźń pozwala to po prostu krócej zapisać. Bo razem z deklaracją przyjaźni możesz przecież od razu definicję tej funkcji podać:

class A {
      friend A operator+(int left, const A& right) {
         // i z głowy... delegacja do publicznej jednak te 2 dodatkowe linie hałasu robi
      }
      
};
  1. Czy to jest potrzebne? Dla wielu nie.
  2. Konieczne? Z całą pewnością nie.
  3. Czy wygodniejsze? Dla wielu tak.
  4. Czy poprawne? Z całą pewnością tak. Przecież to jest sztandarowy use-case prezentowany na C++ FAQ przez Marshalla Cline

A przy okazji... osobiście to robię dokładnie to, co napisałeś ;)

  • w operatorach deleguję do publicznych (tak jak to zrobiłeś z funkcją prn(ostream&) )
  • przyjaźni między klasami nie robię, również piszę zagnieżdżone

Ale... ale... są ludzie, którzy nie lubią zagnieżdżonych klas. Różne powody mają, załóżmy, że po prostu nie lubą. No i w C++ nie ma pojęcia modułu, więc ...

O właśnie! Może jest w C++ na to jakiś inny idiom?!

  • Mam bibliotekę.
  • W niej jakąś klasę elementarną (w stylu MySuperString, MySuperNumber, MySuperFile... cokolwiek)
  • Wszędzie w bibliotece chcę w tej klasie elementarnej grzebać: mam dużo klas, dużo algorytmów, dużo plików .cpp
  • Nikt na zewnątrz biblioteki nie powinien w tej klasie elementarnej grzebać

W javie robiłem dostęp package. W C++ kończy się na tym, że MySuperType deklaruje przyjaźń do wszystkich klas jak leci, tych deklaracji jest dużo :(

0

Widzę że lubisz ściemniać:

class A {
      A operator+(int right)const {
         // i z głowy... krótsza forma i bez zaciemnień z przyjaźnią oraz bez dziwnych nieporozumień wynikających
      } 
};

1 użytkowników online, w tym zalogowanych: 0, gości: 1