2012-10-10 19 views
5

W książce Język programowania w C++, autor: Bjarne Stroustrup, autor wprowadza macierz klasy, która musi zaimplementować funkcję inv(). W sekcji 11.5.1 mówi o dwóch możliwościach. Jedną z nich jest funkcja członkowska, a druga to utworzenie funkcji przyjaźni inv(). Następnie pod koniec sekcji 11.5.2, w którym mówi o dokonywania wyboru czy używać znajomego lub funkcji składowej, mówi:Czy wybrać funkcję członka lub funkcję znajomego, gdy funkcja ma zmienić stan obiektu?

Jeśli inv() naprawdę robi sama, raczej inwertowany Matrix m niż zwrócenie nowej Macierzy, która jest odwrotnością m, powinna być członkiem.

Dlaczego tak jest? Czy funkcja znajomego nie może zmienić stanu macierzy i zwrócić odniesienia do tej macierzy? Czy to ze względu na możliwość przechodzenia tymczasową matrycę kiedy należy wywołać funkcję? ..

+1

Prawdopodobnie lepiej zdefiniowane enkapsulowanie funkcjonalności wewnątrz obiektu zamiast rozprzestrzeniania się w różnych funkcjach znajomych. – PherricOxide

+2

Powiązane http://stackoverflow.com/a/7821482/46642 –

+0

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". –

Odpowiedz

9

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.

+0

Dostarczyłeś jeden dość zawarty przypadek funkcji znajomego, ale co z tym, kiedy ktoś robi z klasy przyjaciela? Można argumentować, że faktycznie zmniejsza to, a nawet przerywa hermetyzację. – Brady

+0

To, o co warto pytać, dotyczy funkcji członka a funkcji znajomego (nie ma żadnego sensu dla klas przyjaciół). Gdyby chodziło o klasy, nadal potrzebowałbym jednego przykładu, aby i tak przyjąć to twierdzenie. –

+0

Dobra uwaga. Moja odpowiedź dotyczyła ogólnie znajomych (klasa i funkcja). STL używa funkcji znajomych w kilku miejscach, ale nie sądzę, że używa klas przyjaciół. – Brady

0

Filozofia enkapsulacji nieodłącznym OOD (C++, który stara się promować) mówi, że stan obiektu może być modyfikowana tylko od wewnątrz. Jest poprawny pod względem składni (na to pozwala kompilator), ale należy go unikać.

Jest podatny na błędy, aby elementy w całym systemie mogły się nawzajem zmieniać bez korzystania z predefiniowanych interfejsów. Przechowywanie obiektów i ich funkcjonalność mogą się zmieniać, a rozglądanie się za kodem (który może być ogromny), który używa określonej części obiektu, byłoby koszmarem.

0

Istnieją dwa przeciwstawne argumenty dotyczące wykorzystujących przyjaciółmi:

Jedna strona mówi znajomych zmniejsza enkapsulacji bo teraz pozwalasz podmioty zewnętrzne dostęp wnętrzności klasy i wewnętrzne powinny być modyfikowane tylko metodami członkowskich.

Druga strona mówi, że znajomi mogą faktycznie zwiększyć hermetyzację, ponieważ możesz udostępnić elementy wewnętrzne klasy niewielkiemu zestawowi jednostek zewnętrznych, eliminując w ten sposób potrzebę udostępnienia atrybutów klas wewnętrznych wszystkim, aby te zewnętrzne jednostki może uzyskać do nich dostęp/nimi manipulować.

Obie strony można argumentować, ale zgadzam się z pierwszą opcją.

Jeśli chodzi o twoje pytanie, to jako PherricOxide wspomniano w jego komentarzu: Jeśli wewnętrzne atrybuty klasy muszą zostać zmodyfikowane, to lepiej, aby było to zrobione przez metodę członka, a tym samym wymuszenie enkapsulacji. Jest to zgodne z pierwszą opcją wspomnianą powyżej.

+0

Dlaczego w dół? – Brady

Powiązane problemy