Szczerze mówiąc, myślę, że jedynym powodem podjęcia takiej decyzji jest syntaktyczna wygoda i tradycja. Wyjaśnię dlaczego, pokazując, jakie są (nie) różnice między tymi dwoma i jak te różnice mają znaczenie przy podejmowaniu decyzji.
Jakie są różnice między funkcjami przyjacielskimi niebędącymi członkami a funkcjami publicznymi? Niewiele. W końcu funkcja członka jest zwykłą funkcją z ukrytym parametrem i dostępem do prywatnych członków klasy.
// what is the difference between the two inv functions?
// --- our code ---
struct matrix1x1 { // this one is simple :P
private:
double x;
public:
//... blah blah
void inv() { x = 1/x; }
friend void inv(matrix1x1& self) { self.x = 1/self.x; }
};
matrix1x1 a;
// --- client code ---
// pretty much just this:
a.inv();
// vs this:
inv(a);
void lets_try_to_break_encapsulation(matrix1x1& thingy) {
thingy.x = 42; // oops, error. Nope, still encapsulated.
}
Oboje zapewniają taką samą funkcjonalność, oraz w żaden sposób nie zmieniają one inne funkcje, co może zrobić. Te same elementy wewnętrzne są wystawione na świat zewnętrzny: nie ma różnicy w zakresie enkapsulacji.Nie ma absolutnie nic, co inne funkcje mogą zrobić inaczej, ponieważ istnieje funkcja znajomego, która modyfikuje stan prywatny.
W rzeczywistości można napisać większość klas z większością funkcji jako funkcje przyjaciół nie będące członkami (funkcje wirtualne i niektóre przeciążone operatory muszą być członkami) zapewniające dokładnie taką samą ilość enkapsulacji: użytkownicy nie mogą pisać żadnej innej funkcji znajomego bez modyfikowania klasa i żadna funkcja inna niż funkcje znajomego nie ma dostępu do prywatnych członków. Dlaczego tego nie robimy? Ponieważ byłoby to sprzeczne ze stylem 99,99% programistów C++ i nie można z niego czerpać wielkich korzyści.
Różnice wynikają z charakteru funkcji i sposobu ich wywoływania. Bycie funkcją członka oznacza, że można uzyskać wskaźnik do funkcji członka z niego, a będąc funkcją nie będącą członkiem, można uzyskać wskaźnik funkcji. Ale rzadko jest to istotne (zwłaszcza w przypadku ogólnych typów obwolut, takich jak std::function
).
Pozostała różnica jest składniowa. Projektanci języka D postanowili po prostu zunifikować całość i powiedzieć, że można wywołać funkcję członka bezpośrednio, przekazując mu obiekt taki jak inv(a)
i wywołać funkcję bezpłatną jako członek jej pierwszego argumentu, na przykład a.inv()
. I żadna klasa nagle nie została źle uwięziona z tego powodu lub czegokolwiek.
Aby zająć się konkretnym przykładem w pytaniu, należy inv
być członkiem lub osobą niebędącą członkiem? Prawdopodobnie stałbym się członkiem, z powodu argumentu o charakterze familijnym, który opisałem powyżej. Nie stylistycznie nie ma to znaczenia.
. Jest to mało prawdopodobne w C++, ponieważ w tym momencie byłaby to przełomowa zmiana, bez znaczących korzyści. Byłby to, na przykład, złamać klasę matrix1x1
, którą napisałem powyżej, ponieważ powoduje ona, że oba połączenia są niejednoznaczne.
Prawdopodobnie lepiej zdefiniowane enkapsulowanie funkcjonalności wewnątrz obiektu zamiast rozprzestrzeniania się w różnych funkcjach znajomych. – PherricOxide
Powiązane http://stackoverflow.com/a/7821482/46642 –
Myślę, że odpowiedzi godne przegranych tutaj powinny wskazywać rzeczywiste różnice między funkcjami członka a przyjaciółmi nie będącymi członkami oraz wyjaśnić, w jaki sposób różnice te przyczyniłyby się do podjęcia decyzji, a nie pomaluj wszystko "ponieważ zapewnia więcej/mniej enkapsulacji". –