• Najnowsze pytania
  • Bez odpowiedzi
  • Zadaj pytanie
  • Kategorie
  • Tagi
  • Zdobyte punkty
  • Ekipa ninja
  • IRC
  • FAQ
  • Regulamin
  • Książki warte uwagi

Przeciążanie operatorów w C++. Co oznacza ta linia?

0 głosów
209 wizyt
pytanie zadane 29 lipca 2021 w C i C++ przez Marcinnok Użytkownik (780 p.)

Witam,
Ostatnio szukam informacji na temat operatorów << i >>. Chodzi o ich używanie poza cout i cin.
Natrafiłem na wpis, że to są normalne operatory, które można sobie dostosować pod funkcję/klasę czy obiekt.
Nie rozumiem za bardzo tego tematu. Czy może ktoś mi to wyjaśnić?

W odpowiedzi na pytanie na StackOverflow (link)  jest linia kodu (poniżej podana) gdzie używane są operatory &. O ile jestem w stanie zrozumieć część tego kodu, to nie wiem w jakim kontekście są używane znaki &. Jako wskaźnik czy co?

template<typename T>
std::vector<T> & operator<<(std::vector<T> & v, T const & item)

Przyznam, że temat wektorów mam nieprzepracowany, więc może dlatego nie rozumiem tego. Jeśli ktokolwiek może pokazać przeciążanie operatorów na przykładzie normalnych zmiennych czy klas, to byłbym wdzięczny.
Z góry dziękuję za każdą odpowiedź czy wskazówkę!

komentarz 29 lipca 2021 przez tkz Nałogowiec (41,900 p.)

https://stackoverflow.com/questions/57483/what-are-the-differences-between-a-pointer-variable-and-a-reference-variable-in

To takiej jakby wskaźniki, ale lepsze pod wieloma względami. W sumie link to tłumaczy. 

Z racji, że pytanie "dlaczego jest tam referencja?" wydaje mi się czubkiem góry lodowej, to odpowiem tutaj. Bo chcesz działać na oryginalnej wartości. 

Jeżeli chodzi o zwracanie referencji. Wyobraź sobie, że masz vec<<2<<3<<4<<5; i niby jak to kompilator przetworzyć, skoro vec masz tylko raz? A no, jest to "składane" ((((vec<<2)<<3)<<4)<<5). Pierwszy nawias zwróci REFERENCJĘ do vec, następy również i następny też, aż do ostatniego. Pozwoli to dalszym operatorom działać na oryginale.

komentarz 2 sierpnia 2021 przez Marcinnok Użytkownik (780 p.)

Okej, to w miarę zrozumiałe, tylko dlaczego w typie zwracanym jest operator &?
 

std::vector<T> & operator<<(std::vector<T> & v, T const & item)

Czy on zwraca referencje do operatora << w postaci wektora? Czy tak to rozumieć?

1 odpowiedź

0 głosów
odpowiedź 29 lipca 2021 przez Tomasz Sobczak Bywalec (2,830 p.)

 

#include <iostream>
#include <string>


class CT_Wypisz
{
    friend std::string& operator>>( const CT_Wypisz& obj_A , std::string& string_Ref_A );      //::  ( 1 )

protected:
    std::string tekst {};                                                                      //::  ( 2 )

public:
    CT_Wypisz() = default;
    CT_Wypisz( const std::string& tekst_A ) : tekst( tekst_A ) {}

    void wypiszTekst() { std::cout << "Aktualny tekst to: " << tekst << std::endl; }           

    CT_Wypisz& operator= ( const std::string& tekst_A )                                        //::  ( 3 )
    { 
        tekst = tekst_A;
        return *this;
    }

    std::string operator+ ( const std::string& tekst_A )                                       //::  ( 4 )
    { 
        return ( tekst + tekst_A );
    }

    CT_Wypisz& operator+= ( const std::string& tekst_A )                                       //::  ( 5 )
    { 
        tekst += tekst_A;
        return *this;
    }

} wypisywacz = std::string{ "fff" };                                                           //::  ( 6 )

std::string& operator>>( const CT_Wypisz& obj_A , std::string& string_Ref_A )                  //::  ( 7 )
{
    string_Ref_A = obj_A.tekst;
    return string_Ref_A;
}
 
int main()
{
    wypisywacz. wypiszTekst();

    std::string tekstString {};

    wypisywacz = "Hello";                                                                    //::  ( 8 )
    wypisywacz. wypiszTekst();

    wypisywacz = wypisywacz + " World";                                                      //::  ( 9 )
    wypisywacz. wypiszTekst();

    ( wypisywacz += "!" ). wypiszTekst();                                                    //::  ( 10 )

    wypisywacz >> tekstString;
    std::cout << "String to teraz: " << tekstString << std::endl;                            //::  ( 11 )
    
    return 0;
}



//::  *( 1 )* -  Deklaracja przyjazni z operatorem>>. Dzięki deklaracji przyjaźni operator będzie mógł korzystać z private i protected składników klasy.  Operator>> jest dwuargumentowy, a jego wywołanie wygląda tak "arg1 >> arg2", a jeżeli chcemy aby działał też tak " string& >> const CT_Wypisz& ", to należy napisać jeszcze jedno przeładowanie.

//::  *( 2 )* -  Ta zmienna jest protected, czyli może być używana bezpośrednio tylko przez funkcje składowe i funkcje zaprzyjaźnione.


//::  *( 3 )* -  Operator=( arg2 ) - Operator dwuargumentowy. Mimo iż jako argument ma zapisany tylko "const std::string&" to jego użycie wymagha dwóch argumentów " CT_Wypisz&  =  const std::string& )


//::  *( 4 )* -  CT_Wypisz&  +  const std::string&  -> Rezultatem bedzie nowoutworzony obiekt tymczasowy typu string, którego w ( 9 ) od razu przypiszemy do wypisywacza korzystając z dodanej już możliwości przypisywania stringów.


//::  *( 5 )* -  CT_Wypisz&  +=  const std::string&   -> Rezultatem będzie referencja do obiektu CT_Wypisz&, w naszym przypadku to referencja do siebie "& *this", ale równie dobrze może to być referencja do innego obiektu. 


//::  *( 6 )* -  Tutaj to nie przypisanie za pomocą operatora= ! Tutaj to wywołanie konstruktora.


//::  *( 7 )* -  Niektóre operatory można tworzyć poza klasą, jako funcja nieskładowa.


//::  *( 8 )* -  Skorzystanie z dobrodziejstwa operatora=.


//::  *( 9 )* -  Najpierw wywołanie operatora+ a nastepnie operatora=. Kolejność wywołania operatorów zależy od ich priorytetów. Priorytety dzielą się na 19 grup w których znajdują się dane operatory. 


//::  *( 10 )* - Użycie operatora+= i dowód na to, że rezultatem jest "CT_Wypisz&" ponieważ bezpośrednio po zakonczeniu wywołania operatora jest wywoływana funkcja składowa.


//::  *( 11 )* - Nasz oberator, który został zdefiniowany jako funkcja nie składowa.


To było tylko kilka przykładów użycia. Zanim zaczniesz używać ich w swoich projektach zalecam dogłębne poznanie ich działania, każdego operatora z osobna. Rezultatem mojej odpowiedzi nie miało być dogłębne wyczerpania tematu operatorów lecz tylko pokazanie ich praktycznego użycia, oraz to jak można skorzystać ze zwróconej przez operator referencji do obiektu.  

1
komentarz 29 lipca 2021 przez tkz Nałogowiec (41,900 p.)

//::  *( 1 )* -  Deklaracja przyjazni z operatorem>>. Dzięki deklaracji przyjaźni operator będzie mógł korzystać z private i protected składników klasy.  Operator>> jest dwuargumentowy, a jego wywołanie wygląda tak "arg1 >> arg2", a jeżeli chcemy aby działał też tak " string& >> const CT_Wypisz& ", to należy napisać jeszcze jedno przeładowanie.

I pięknie rozwalasz hermetyzacje. Abstrahując, że wiele standardów zabrania nawiązywania znajomości w ten sposób. 

//::  *( 3 )* -  Operator=( arg2 ) - Operator dwuargumentowy. Mimo iż jako argument ma zapisany tylko "const std::string&" to jego użycie wymagha dwóch argumentów " CT_Wypisz&  =  const std::string& )

Łamiesz zasadę 0, 3, 5. Jest to operator jednoargumentowy. Bo przyjmuje jeden argument. Po prostu syntaktycznie nie byłby poprawny. 

//::  *( 4 )* -  CT_Wypisz&  +  const std::string&  -> Rezultatem bedzie nowoutworzony obiekt tymczasowy typu string, którego w ( 9 ) od razu przypiszemy do wypisywacza korzystając z dodanej już możliwości przypisywania stringów.

Tak na prawdę nie będzie tymczasowy. Od c++ 17 RVO jest czymś out-the-box.

//::  *( 5 )* -  CT_Wypisz&  +=  const std::string&   -> Rezultatem będzie referencja do obiektu CT_Wypisz&, w naszym przypadku to referencja do siebie "& *this", ale równie dobrze może to być referencja do innego obiektu. 

Właściwie musisz mieć bardzo dobry powód by zwracać referencję do czegoś innego. Chociaż i z tym powodem bym dyskutował. 

//::  *( 6 )* -  Tutaj to nie przypisanie za pomocą operatora= ! Tutaj to wywołanie konstruktora.

Wcale nie. Jest to przypisanie, które jest rodzajem konstruktora. 

//::  *( 7 )* -  Niektóre operatory można tworzyć poza klasą, jako funcja nieskładowa.

Co imo jest lepsze od friend. Szczególnie gdy dodamy ładny namespace. 

 

komentarz 29 lipca 2021 przez Tomasz Sobczak Bywalec (2,830 p.)

Ad 1. Może nie wybrałem najlepszej metody, ale to metoda bezpieczna gdyż pozwala tylko odczytywać wartości, nie pozwala na modyfikację. 

 

Ad. 2. Miałem na myśli  operator dwuoperandowe. Nie władam biegle wszystkimi nazwami. Mój przykład mnie broni, pozwala zrozumieć, że użyłem złego słowa.

Ad 3. Ok.

Ad 4. Należy pokazać, że taka możliwość też jest. Nie należy się zamykać na możliwości.

Ad 5. W każdym bądź razie bez odpowiedniego konstruktora to nie zadziała.

 

 
1
komentarz 30 lipca 2021 przez TOM_CPP Pasjonat (22,620 p.)
edycja 30 lipca 2021 przez TOM_CPP

@tkz,

//::  *( 6 )* -  Tutaj to nie przypisanie za pomocą operatora= ! Tutaj to wywołanie konstruktora.

-------------------------------------------------------------------------------------

Wcale nie. Jest to przypisanie, które jest rodzajem konstruktora. 

Akurat w tym przypadku wywołany zostanie konstruktor klasy CT_Wypisz. https://godbolt.org/z/f67j67a64

Przypisanie jest wywoływane na rzecz obiektu już istniejącego. Jeżeli operator przypisania występuje w momencie tworzenia obiektu ( jak w powyższym kodzie ), to zostanie wywołany jeden z konstruktorów klasy.

komentarz 31 lipca 2021 przez tkz Nałogowiec (41,900 p.)

Ad 1. Może nie wybrałem najlepszej metody, ale to metoda bezpieczna gdyż pozwala tylko odczytywać wartości, nie pozwala na modyfikację. 

Ale nadal hermetyzacja leci na łeb. Nie na tym polega korzystanie z klas. 

Ad 4. Należy pokazać, że taka możliwość też jest. Nie należy się zamykać na możliwości.

Jeżeli odnosisz się do mojego komentarza, co do zwracania nie-siebie. To tak, należy się zamykać na takie sztuczki. Ale warto to pokazać, jak napisałeś. 

@TOM_CPP fakt. Mój błąd. 

Podobne pytania

0 głosów
1 odpowiedź 492 wizyt
+1 głos
0 odpowiedzi 100 wizyt
0 głosów
1 odpowiedź 350 wizyt
pytanie zadane 27 maja 2018 w C i C++ przez krystian1997 Obywatel (1,020 p.)

88,701 zapytań

137,308 odpowiedzi

306,748 komentarzy

58,894 pasjonatów

Motyw:

Akcja Pajacyk

Pajacyk od wielu lat dożywia dzieci. Pomóż klikając w zielony brzuszek na stronie. Dziękujemy! ♡

Sklep oferujący ćwiczenia JavaScript, PHP, rozmowy rekrutacyjne dla programistów i inne materiały

Oto dwie polecane książki warte uwagi. Pełną listę znajdziesz tutaj.

...