funkcje friend w klasie

0

cześć wszystkim mam pytanie odnośnie funkcji friend w klasach bo gdzieś na forum przeczytałem że jest to łamanie zasad hermetyzacji? - ograniczania dostępu do obiektu i nie powinno się ich stosować natomiast w książce szkoła programowania c++ autor zaleca ich stosowanie co więcej podaje że jeżeli chodzi o niektóre wersje przeciążonych operatorów jest to jedyne możliwe wyjście np jeżeli pierwszym argumentem który przyjmuje operator ma być obiekt inny od tego w jakiej funkcja friend się znajduje np ostream przykładowy kod

#include <iostream>
#include <fstream>
#include <cstdlib>
using std::cin;
using std::cout;
using std::endl;
using std::string;
using std::istream;
using std::ostream;

class c_root
{
    private:
        string name;
        string ID;
    public:
        c_root();
        c_root(string n1, string n2);
        friend istream & operator>>(istream & in, c_root & n1);
        friend ostream & operator<<(ostream & out, const c_root & n1);
        
};

c_root::c_root() : name(""), ID("")
{
    //--------------------
}

c_root::c_root(string n1, string n2) : name(n1), ID(n2)
{
    //--------------------
}

istream & operator>>(istream & in, c_root & n1)
{
    cout<< "name: "; in>> n1.name;
    cout<< "ID: ";   in>> n1.ID;
    return in;
}

ostream & operator<<(ostream & out, const c_root & n1)
{
    out<< "name: " << n1.name <<endl;
    out<< "ID: " << n1.ID <<endl;
    return out;
}

więc jak to wygląda w praktyce?

0
emacs napisał(a):

cześć wszystkim mam pytanie odnośnie funkcji friend w klasach bo gdzieś na forum przeczytałem że jest to łamanie zasad hermetyzacji? - ograniczania dostępu do obiektu i nie powinno się ich stosować natomiast w książce szkoła programowania c++ autor zaleca ich stosowanie co więcej podaje że jeżeli chodzi o niektóre wersje przeciążonych operatorów jest to jedyne możliwe wyjście np jeżeli pierwszym argumentem który przyjmuje operator ma być obiekt inny od tego w jakiej funkcja friend się znajduje np ostream przykładowy kod ...

Jak komu pasuje. Osobiście uważam, że uzasadnienie tego "autora" jest bezsensu, bo wcale nie jest to "jedyne możliwe wyjście".
Moim zdaniem najlepiej zawsze najpierw zdefiniować normalną metodę implementującą funkcjonalność, którą ma mieć dany operator, a następnie definiować prosty operator inline'owo, kóry jedynie wywołuję tę metodę. W ten sposób, przyjaźnie stają się zbędne, operator zawsze można potem poprawić (nie trzeba się martwić o binary compability operatora, a jedynie o code compability).


przeróbka twojego przykładu: ```cpp class c_root { private: string name; string ID; public: c_root(); c_root(string n1, string n2);
    istream & readFromStream(istream & in);
    ostream & wrteToStream(ostream & out) const;

};

inline istream & operator>>(istream & in, c_root & n1) {
return n1.readFromStream(in);
}

inline ostream & operator<<(ostream & out, const c_root & n1) {
return n1.wrteToStream(out);
}
// reszta jest oczywista
...

0

ja uważam że zaprzyjaźnionych funkcji należy unikać bo chyba prawie zawsze logikę programu idzie zapisać inaczej , no chyba ,że mamy przeładowany operator dla jakiejś klasy i chcemy odnieść się do prywatnych pól lub metod. Jednak pamiętajmy żeby nie nadużywać funkcji zaprzyjaźnionych bo zrobi się burdel

0

dzięki za szybką odpowiedź.... marekR22 bardzo fajne rozwiązanie szkoda że w książce o nim nie wspomnieli... nasuwa mi się jeszcze jedno pytanie
czy jeżeli wszystkie metody w obiektach odpowiedzialne za wczytanie/wyświetlenie ich danych nazywałbym tak samo np read i write to mógłbym w celu wygody użyć 2 szablów funkcji dla >> i << ? bo bez szablonów musiałbym dla każdego obiektu pisać osobne 2 funkcje inline.... napisałem coś takiego

template <typename ci>
inline istream & operator>>(istream & in, ci & n1) {
    return n1.read(in);
}

template <typename co>
inline ostream & operator<<(ostream & out, const co & n1) {
    return n1.write(out);
}


int main()
{
    c_personal zmienna1;
    c_root zmienna2;
    
    cin>> zmienna1 >> zmienna2;
    cout<< zmienna1 <<zmienna2 <<endl;
            
    system("PAUSE");
}

sprawdziłem to w programie i działa tak jak ma tylko teraz proszę powiedzcie mi czy jest to dobre rozwiązanie i mogę je stosować?

0

Rozwiązanie złe nie jest, ale ograniczasz się do tego, że tym obiektu musi być znany w czasie kompilacji.
Imho lepie by było zrobić interfejs, który odpowiednie klasy by implementowały.

class IStreamReadWrite
{
public:
    istream& read(istream & in) = 0;
    ostream& write(ostream & out) = 0;
};

inline istream& operator>>(IStreamReadWrite &obj)
{
    return obj.write(in);
}
//analogicznie drugi operator
class Test : public IStreamReadWrite
{
    ...
    istream& read(istream & in)
    {
        ...
    }
    //analogicznie druga funkcja
}

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