To jest w zasadzie pytanie, czy istnieje „właściwy” sposób wdrożenia operator<<
? Czytając to widzę, że coś takiego:
friend bool operator<<(obj const& lhs, obj const& rhs);
jest lepszy od czegoś takiego jak
ostream& operator<<(obj const& rhs);
Ale nie do końca rozumiem, dlaczego powinienem używać jednego lub drugiego.
Mój osobisty przypadek to:
friend ostream & operator<<(ostream &os, const Paragraph& p) {
return os << p.to_str();
}
Ale prawdopodobnie mógłbym zrobić:
ostream & operator<<(ostream &os) {
return os << paragraph;
}
Na jakiej podstawie powinienem oprzeć tę decyzję?
Uwaga :
Paragraph::to_str = (return paragraph)
gdzie akapit to ciąg.
c++
operator-overloading
Federico Builes
źródło
źródło
Odpowiedzi:
Problem polega na interpretacji linku do artykułu .
Równość
Ten artykuł dotyczy kogoś, kto ma problemy z poprawnym zdefiniowaniem operatorów relacji typu bool.
Operator:
Te operatory powinny zwracać wartość bool, ponieważ porównują dwa obiekty tego samego typu. Zwykle najłatwiej jest zdefiniować te operatory jako część klasy. Dzieje się tak, ponieważ klasa jest automatycznie zaprzyjaźniona z samą sobą, więc obiekty typu Paragraf mogą sprawdzać się nawzajem (nawet prywatni członkowie).
Istnieje argument przemawiający za tworzeniem tych wolnostojących funkcji, ponieważ umożliwia to automatyczną konwersję obu stron, jeśli nie są one tego samego typu, podczas gdy funkcje składowe pozwalają tylko na automatyczną konwersję prawej strony. Uważam to za argument papiernika, ponieważ tak naprawdę nie chcesz, aby automatyczna konwersja miała miejsce w pierwszej kolejności (zwykle). Ale jeśli jest to coś, czego chcesz (nie polecam tego), wtedy wolnostojące komparatory mogą być korzystne.
Streaming
Operatorzy strumieni:
Kiedy używasz ich jako operatorów strumienia (zamiast przesunięcia binarnego), pierwszym parametrem jest strumień. Ponieważ nie masz dostępu do obiektu strumienia (nie możesz go modyfikować), nie mogą być operatorami składowymi, muszą one być zewnętrzne w stosunku do klasy. W związku z tym muszą albo być przyjaciółmi klasy, albo mieć dostęp do publicznej metody, która przeprowadzi transmisję strumieniową za Ciebie.
Te obiekty również tradycyjnie zwracają odwołanie do obiektu strumienia, dzięki czemu można łączyć operacje strumieniowe.
źródło
operator<<
private:
?freiend
jest sposobem na rozszerzenie publicznego interfejsu bez przerywania enkapsulacji. Przeczytaj programmers.stackexchange.com/a/99595/12917Nie możesz tego zrobić jako funkcji składowej, ponieważ niejawny
this
parametr znajduje się po lewej stronie<<
-operator. (W związku z tym należałoby dodać go jako funkcjęostream
składową do -klasy. Niedobrze :)Czy mógłbyś to zrobić jako darmową funkcję bez
friend
tego? Wolę to, ponieważ jest jasne, że jest to integracja zostream
podstawową funkcjonalnością Twojej klasy, a nie podstawową funkcjonalność.źródło
friend
Funkcja ma takie same prawa jak funkcję elementu ( to jest to, cofriend
znaczy), więc jako użytkownik klasy, musiałbym się zastanawiać, dlaczego to potrzebujemy. To jest różnica, którą próbuję wprowadzić, używając sformułowania „podstawowa funkcjonalność”.Jeśli to możliwe, jako osoby niebędące członkami i nieprzyjazne.
Jak opisali Herb Sutter i Scott Meyers, preferuj nieprzyjazne funkcje niebędące członkami od funkcji składowych, aby pomóc zwiększyć hermetyzację.
W niektórych przypadkach, takich jak strumienie C ++, nie będziesz mieć wyboru i musisz używać funkcji niebędących składowymi.
Nie oznacza to jednak, że musisz uczynić te funkcje przyjaciółmi swoich klas: te funkcje mogą nadal uzyskiwać dostęp do Twojej klasy za pośrednictwem metod dostępu do Twojej klasy. Jeśli uda ci się napisać te funkcje w ten sposób, wygrałeś.
O prototypach operatorów << i >>
Uważam, że przykłady, które podałeś w swoim pytaniu, są błędne. Na przykład;
Nie mogę nawet zacząć myśleć, jak ta metoda mogłaby działać w strumieniu.
Oto dwa sposoby implementacji operatorów << i >>.
Załóżmy, że chcesz użyć obiektu podobnego do strumienia typu T.
I chcesz wyodrębnić / wstawić z / do T odpowiednie dane swojego obiektu typu Paragraf.
Prototypy funkcji operatora ogólnego << i >>
Pierwsza istota jako funkcje:
Operator ogólny << i >> prototypy metod
Druga istota jako metody:
Zauważ, że aby użyć tej notacji, musisz rozszerzyć deklarację klasy T. W przypadku obiektów STL nie jest to możliwe (nie należy ich modyfikować ...).
A co, jeśli T jest strumieniem w C ++?
Oto prototypy tych samych operatorów << i >> dla strumieni C ++.
Dla ogólnych basic_istream i basic_ostream
Zauważ, że jest to przypadek strumieni, ponieważ nie możesz modyfikować strumienia C ++, musisz zaimplementować funkcje. Co oznacza coś takiego:
Dla węgli i ostremów
Poniższy kod będzie działał tylko w przypadku strumieni opartych na znakach.
Rhys Ulerich skomentował fakt, że kod oparty na znakach jest tylko „specjalizacją” kodu ogólnego nad nim. Oczywiście Rhys ma rację: nie polecam używania przykładu opartego na znakach. Jest tu podane tylko dlatego, że jest łatwiejsze do odczytania. Ponieważ jest to możliwe tylko wtedy, gdy pracujesz tylko ze strumieniami opartymi na znakach, powinieneś unikać go na platformach, na których kod wchar_t jest powszechny (np. W systemie Windows).
Mam nadzieję, że to pomoże.
źródło
Powinien zostać zaimplementowany jako bezpłatne, nieprzyjazne funkcje, zwłaszcza jeśli, jak większość rzeczy w dzisiejszych czasach, dane wyjściowe są głównie używane do diagnostyki i logowania. Dodaj metody dostępu const do wszystkich rzeczy, które mają trafić do wyniku, a następnie niech moduł wyjściowy po prostu je wywoła i sformatuje.
Właściwie zabrałem się do zebrania wszystkich tych wolnych funkcji wyjściowych ostream w nagłówku i pliku implementacji „ostreamhelpers”, dzięki czemu ta dodatkowa funkcjonalność jest daleko od rzeczywistego celu klas.
źródło
Podpis:
Wydaje się raczej podejrzane, to nie pasuje do
stream
konwencji ani do konwencji bitowej, więc wygląda na to, że nadużycie operatoraoperator <
powinno powrócić,bool
aleoperator <<
prawdopodobnie powinno zwrócić coś innego.Jeśli tak chciałeś, powiedz:
Ponieważ nie możesz z
ostream
konieczności dodawać funkcji, funkcja musi być funkcją wolną, niezależnie od tego, czy jestfriend
ona zależna od tego, do czego ma dostęp (jeśli nie ma dostępu do członków prywatnych lub chronionych, nie ma potrzeby przyjaciel).źródło
ostream
że przy korzystaniu zostream.operator<<(obj&)
zamówienia wymagany byłby dostęp do modyfikacji ; stąd wolna funkcja. W przeciwnym razie użytkownik musi być typem pary, aby zapewnić dostęp.Na koniec chciałbym dodać, że rzeczywiście możesz stworzyć operator
ostream& operator << (ostream& os)
wewnątrz klasy i to może działać. Z tego co wiem, używanie go nie jest dobrym pomysłem, ponieważ jest bardzo zawiłe i nieintuicyjne.Załóżmy, że mamy ten kod:
Podsumowując - dasz radę, ale raczej nie powinieneś :)
źródło
operator przyjaciel = równe prawa jak klasa
źródło
operator<<
zaimplementowana jako funkcja znajomego:Może to być funkcja zaprzyjaźniona tylko dlatego, że obiekt znajduje się po prawej stronie,
operator<<
a argumentcout
po lewej stronie. Więc nie może to być funkcja składowa klasy, może to być tylko funkcja zaprzyjaźniona.źródło