Przeładowywanie operatorów

FaTaL.92

1 Wstęp
2 Operacje na obiektach
     2.1 operator>>
3 Operatory binarne (Binary Operators)
     3.2 operator+
     3.3 operator=
4 Operatory jednoskładnikowe (Unary Operators)
     4.4 operator! i operator bool
5 Inne operatory
     5.5 operator[]
     5.6 operator-
     5.7 operator*
     5.8 operator/
     5.9 operator%
     5.10 operator^
     5.11 operator&
     5.12 operator|
     5.13 operator~
     5.14 operator!
     5.15 operator
     5.16 operator>
     5.17 operator+=
     5.18 operator-=
     5.19 operator*=
     5.20 operator/=
     5.21 operator%=
     5.22 operator^=
     5.23 operator&=
     5.24 operator|=
     5.25 operator<
     5.26 operator>>
     5.27 operator<=
     5.28 operator>>=
     5.29 operator==
     5.30 operator!=
     5.31 operator<=
     5.32 operator>=
     5.33 operator&&
     5.34 operator||
     5.35 operator++
     5.36 operator--
     5.37 operator->

Wstęp

Przeładowywanie operatorów, zamiennie można używać słowa przeciążanie, wykorzystuje się dla struktur lub klas. Nie można ich przeciążyć gdzie inaczej niż tam. Może przejdźmy od razu do przykładów.

Operacje na obiektach

A mianowicie mamy kod: ```cpp #include <iostream> using namespace std;

class Foo
{
public:
int pierwsza;
int druga;
};
int main()
{
Foo ex1,ex2,ex3;

ex1.pierwsza = 1;
ex2.pierwsza = 2;
ex1.druga = 3;
ex2.druga = 4;

ex3=ex1+ex2;

system("pause>nul");

}

I niby wszystko było by pięknie ale niestety kompilatorowi nie pasuje.

no match for 'operator+' in 'ex1 + ex2'

<font size="2"><b>Dlaczego?</b></span>
A no między innymi dla tego że nie wie co dodać i jak.
Musimy dać mu informację co ma dodać.
W powyższym przykładzie dla naszej klasy potrzebujemy przeciążenia dwóch operatorów (przypisania i dodawania).

<h1>Operatory których nie można przeciążać</h1>
Istnieje kilka operatorów, których nie można przeciążać, a mianowicie:

. (kropka)
.*
?:
:: (operator zakresu)
sizeof


<h1>Strumienie</h1>
<h2>operator<< </h2>
Aby zaoszczędzić czas i zyskać na szybkości programu możemy przeciążyć operator strumienia wyjścia dla naszej klasy.
Przykładowo żeby wyświetlić składniki naszej klasy <i>Foo</i> możemy napisać coś w tym stylu: (wykorzystujemy klasę i inicjację z przykładu wyżej oczywiście oprócz operacji przypisania i sumowania, tym zajmiemy się później)
```cpp
cout << "Pierwsza liczba: " << ex1.pierwsza << " oraz druga: " << ex1.druga << endl;
cout << "Pierwsza liczba: " << ex2.pierwsza << " oraz druga: " << ex2.druga << endl;

Ale co jeśli będziemy mieli tablicę stu lub tysiąca takich obiektów ?
Kod stanie się dość nieczytelny i bardzo długi. Oczywiście można w pętli for i bawić się na różne sposoby, ale mamy na to prostą radę - przeciążenie operatora<<.
Oczywiście trzeba nasz przeciążony operator zadeklarować jako metodę klasy Foo. Najczęściej pisze się ją zaprzyjaźnioną z klasą, ponieważ gdy mamy prywatne zmienne klasy, które chcemy wyświetlić, operator nie miał by do nich dostępu.

Oto przykład deklaracji:

friend ostream& operator<< (ostream&,Foo const&);

Natomiast kod naszej metody będzie wyglądał podobnie jakbyśmy pisali wszystko jw.

ostream& operator<< (ostream &wyjscie, Foo const& ex)
{
   wyjscie << "Pierwsza liczba: " << ex.pierwsza << " oraz druga: " << ex.druga << endl;
   return wyjscie;
}

Ale za to skraca nam się to w kodzie i wygląda przejrzyście.

cout << ex1 << ex2;

Efekt jest równoważny temu co z naszego naiwnego wyświetlania elementów funkcji, ale ma więcej zalet.

Zawsze używaj stałej referencji na obiekt klasy, którego elementy wyświetlasz.

operator>>

Przeciążenie operatora strumienia wejścia też może się czasem do czegoś przydać. Ewentualne instrukcje co użytkownik ma podać trzeba napisać przed wczytywaniem. Operator>> także dobrze by było żeby był zaprzyjaźniony z klasą z tych samych powodów co operator<.

Deklaracja oraz kod metody wyglądają również podobnie jak z operatorem wyjścia:

//deklaracja

friend istream& operator>> (istream&,Foo&);

//kod

istream& operator>> (istream &wejscie, Foo& ex)         
{
   wejscie >> ex.pierwsza >> ex.druga;
   return wejscie;
}

//użycie zamiast przypisywania

cout << "Podaj 2 liczby dla:\n"
         << "Obiektu pierwszego: ";
    cin >> ex1;
    cout << "Oraz drugiego: ";
    cin >> ex2;

Tutaj nie możesz używać stałej referencji, ponieważ zmieniasz wartości elementów obiektu!

Operatory binarne (Binary Operators)

operator+

No i upragniony operator dodawania, co do którego krzyczał kompilator w naszym pierwszym programie. Tutaj sprawa wygląda trochę inaczej, ponieważ metoda zwraca obiekt klasy Foo. Musimy dodatkowo dopisać konstruktor klasy, a dokładniej dwa: jeden potrzebny nam do sumowania a drugi bezargumentowy.

Tak więc nasz program na daną chwilę będzie wyglądał tak:

#include <iostream>
using namespace std;

class Foo
{
 public:
      int pierwsza;
      int druga;
 Foo();
 Foo(int,int);   
      
 friend ostream& operator<< (ostream&,Foo const&);
 friend istream& operator>> (istream&,Foo&);
 Foo operator+ (Foo const&);
};

Foo::Foo()
{
 pierwsza = druga = 0;
}

Foo::Foo(int a,int b)
{
 pierwsza = a;
 druga = b;
}

ostream& operator<< (ostream &wyjscie, Foo const& ex)
{
   wyjscie << "Pierwsza liczba: " << ex.pierwsza << " oraz druga: " << ex.druga << endl;
   return wyjscie;
}

istream& operator>> (istream &wejscie, Foo& ex)         
{
   wejscie >> ex.pierwsza >> ex.druga;
   return wejscie;
}

Foo Foo::operator+ (Foo const& ex)
{    
   Foo tmp(pierwsza + ex.pierwsza, druga + ex.druga);
   return tmp;    
}

int main()
{
    Foo ex1,ex2,ex3;
    
    cout << "Podaj 2 liczby dla:\n"
         << "Obiektu pierwszego: ";
    cin >> ex1;
    cout << "Oraz drugiego: ";
    cin >> ex2;
    
    cout << ex1 << ex2 << ex1+ex2;
    
    system("pause>nul");
}

Kilka linijek wymaga chyba wyjaśnienia.
Co do konstruktorów to jeden musi być bezargumentowy (może nie robić nic), a drugi musi być (u nas akurat) dwuragumentowy żeby stworzyć nowy obiekt będący sumą dwóch.

W kodzie operatora można np. używać instrukcji warunkowych:

Foo Foo::operator+ (Foo const& ex)
{ 
   if(pierwsza && ex.pierwsza > 0)
   {   
     Foo tmp(pierwsza + ex.pierwsza, druga + ex.druga);
     return tmp;
   }
   else
   {
    Foo tmp(-1,-1); //wartość -1 często jest uznawana jako błąd funkcji
    cout << "Blad\n";
    return tmp;  //funkcja musi coś zwracać w innym wypadku wyjdą śmieci
   }
}

Można również jako argument pobierać np. zwykłą liczbę całkowitą i dodawać do któregoś elementu. Wtedy kod wyglądał by tak:

Foo Foo::operator+ (int const& liczba)
{    
   Foo tmp(pierwsza + liczba, druga);
   return tmp;    
}

Reszta zależy już od celu jaki ma dodawanie spełniać.

operator=

No i drugi operator potrzebny nam do podstawowych operacjach na obiektach, a mianowicie operator przypisania. To już powinno być proste. Wszystkie operatory przeciąża się wg jakiegoś schematu. Tutaj obiektowi dla którego wywołujemy operator przypisania po prostu kopiujemy wartości elementów z drugiego obiektu. ```cpp Foo& Foo::operator= (Foo const& ex) { pierwsza = ex.pierwsza; druga = ex.druga; } ```

Operatory jednoskładnikowe (Unary Operators)

operator! i operator bool

Te dwa operatory są po prostu swoją przeciwnością i pokarzę je w jednym przykładzie.
#include <iostream>
using namespace std;

class TablicaInt
    {
       public:
         TablicaInt(int el) : Tab(new int[el]), L_elementow(el) {}

         operator bool () const {return (L_elementow != 0);}
         bool operator! () const {return (L_elementow == 0);}

       private:
         int * Tab;
         int L_elementow;
    };

int main()
{
  int n = 5;
  TablicaInt tab(n);

  if(tab)
    cout << "Tablica nie jest pusta." << endl;
  if(!tab)
    cout << "Tablica jest pusta." << endl;

  TablicaInt tab2(0);

  if(tab2)
    cout << "Tablica nie jest pusta." << endl;
  if(!tab2)
    cout << "Tablica jest pusta." << endl;

  return 0;
}

Po prostu sprawdzają one czy dany element w strukturze/klasie ma wartość prawdziwą czy też nie.

Inne operatory

operator[]

Wykorzystując klasę TablicaInt wystarczy dodać: ```cpp int & operator[](int el) {return Tab[el];} const int & operator[](int el) const {return Tab[el];}

int n = 5;
TablicaInt tab(n);

for(int i = 0; i < n; ++i)
{
tab[i] = i;
cout << tab[i] << endl;
}


<h1>Operatory wcześniej nieopisane</h1>
Poniżej spis wszystkich operatów, które da się przeciążyć i jakim schematem.

<h2>operator+</h2>
<font size="2"><b>w klasie</b></span>
```cpp
_zwracany_typ_ operator+(const _typ_&);

<font size="2">poza klasą</span>

_zwracany_typ_ operator+(const _typ1_&, const _typ2_&);

operator-

<font size="2">w klasie</span> ```cpp _zwracany_typ_operator-(const _typ_&); ``` <font size="2">poza klasą</span> ```cpp _zwracany_typ_ operator-(const _typ1_&, const _typ2_&); ```

operator*

<font size="2">w klasie</span> ```cpp _zwracany_typ_ operator*(const _typ_&); ``` <font size="2">poza klasą</span> ```cpp _zwracany_typ_ operator*(const _typ1_&, const _typ2_&); ```

operator/

<font size="2">w klasie</span> ```cpp _zwracany_typ_ operator/(const _typ_&); ``` <font size="2">poza klasą</span> ```cpp _zwracany_typ_ operator/(const _typ1_&, const _typ2_&); ```

operator%

<font size="2">w klasie</span> ```cpp _zwracany_typ_ operator%(const _typ_&); ``` <font size="2">poza klasą</span> ```cpp _zwracany_typ_ operator%(const _typ1_&, const _typ2_&); ```

operator^

<font size="2">w klasie</span> ```cpp _zwracany_typ_ operator^(const _typ_&); ``` <font size="2">poza klasą</span> ```cpp _zwracany_typ_ operator^(const _typ1_&, const _typ2_&); ```

operator&

<font size="2">w klasie</span> ```cpp _zwracany_typ_ operator&(const _typ_&); ``` <font size="2">poza klasą</span> ```cpp _zwracany_typ_ operator&(const _typ1_&, const _typ2_&); ```

operator|

<font size="2">w klasie</span> ```cpp _zwracany_typ_ operator|(const _typ_&); ``` <font size="2">poza klasą</span> ```cpp _zwracany_typ_ operator|(const _typ1_&, const _typ2_&); ```

operator~

<font size="2">w klasie</span> ```cpp _zwracany_typ_ operator~(); ``` <font size="2">poza klasą</span> ```cpp _zwracany_typ_ operator~(const _typ_&); ```

operator!

<font size="2">w klasie</span> ```cpp _zwracany_typ_ operator!(); ``` <font size="2">poza klasą</span> ```cpp _zwracany_typ_ operator!(const _typ_&); ```

operator

<font size="2">w klasie</span> ```cpp _zwracany_typ_ operator<(const _typ_&); ``` <font size="2">poza klasą</span> ```cpp _zwracany_typ_ operator<(const _typ1_&, const _typ2_&); ```

operator>

<font size="2">w klasie</span> ```cpp _zwracany_typ_ operator>(const _typ_&); ``` <font size="2">poza klasą</span> ```cpp _zwracany_typ_ operator>(const _typ1_&, const _typ2_&); ```

operator+=

<font size="2">w klasie</span> ```cpp _zwracany_typ_ & operator+=(const _typ_&); ``` <font size="2">poza klasą</span> ```cpp _zwracany_typ_ & operator+=(_zwracany_typ_&, const _typ_&); ```

operator-=

<font size="2">w klasie</span> ```cpp _zwracany_typ_ & operator-=(const _typ_&); ``` <font size="2">poza klasą</span> ```cpp _zwracany_typ_ & operator-=(_zwracany_typ_&, const _typ_&); ```

operator*=

<font size="2">w klasie</span> ```cpp _zwracany_typ_ & operator*=(const _typ_&); ``` <font size="2">poza klasą</span> ```cpp _zwracany_typ_ & operator*=(_zwracany_typ_&, const _typ_&); ```

operator/=

<font size="2">w klasie</span> ```cpp _zwracany_typ_ & operator/=(const _typ_&); ``` <font size="2">poza klasą</span> ```cpp _zwracany_typ_ & operator/=(_zwracany_typ_&, const _typ_&); ```

operator%=

<font size="2">w klasie</span> ```cpp _zwracany_typ_ & operator%=(const _typ_&); ``` <font size="2">poza klasą</span> ```cpp _zwracany_typ_ & operator%=(_zwracany_typ_&, const _typ_&); ```

operator^=

<font size="2">w klasie</span> ```cpp _zwracany_typ_ & operator^=(const _typ_&); ``` <font size="2">poza klasą</span> ```cpp _zwracany_typ_ & operator^=(_zwracany_typ_&, const _typ_&); ```

operator&=

<font size="2">w klasie</span> ```cpp _zwracany_typ_ & operator&=(const _typ_&); ``` <font size="2">poza klasą</span> ```cpp _zwracany_typ_ & operator&=(_zwracany_typ_&, const _typ_&); ```

operator|=

<font size="2">w klasie</span> ```cpp _zwracany_typ_ & operator|=(const _typ_&); ``` <font size="2">poza klasą</span> ```cpp _zwracany_typ_ & operator|=(_zwracany_typ_&, const _typ_&); ```

operator<

<font size="2">w klasie</span> ```cpp _zwracany_typ_ & operator<(const _typ_&); ``` <font size="2">poza klasą</span> ```cpp _zwracany_typ_ & operator<(const _zwracany_typ_&, const _typ_&); ```

operator>>

<font size="2">w klasie</span> ```cpp _zwracany_typ_ & operator>>(const _typ_&); ``` <font size="2">poza klasą</span> ```cpp _zwracany_typ_ & operator>>(const _zwracany_typ_&, const _typ_&); ```

operator<=

<font size="2">w klasie</span> ```cpp _zwracany_typ_ & operator<=(const _typ_&); ``` <font size="2">poza klasą</span> ```cpp _zwracany_typ_ & operator<=(_zwracany_typ_&, const _typ_&); ```

operator>>=

<font size="2">w klasie</span> ```cpp

zwracany_typ & operator>>=(const typ&);

<font size="2"><b>poza klasą</b></span>
```cpp
_zwracany_typ_ & operator>>=(_zwracany_typ_&, const _typ_&);

operator==

<font size="2">w klasie</span> ```cpp

zwracany_typ operator==(const typ&);

<font size="2"><b>poza klasą</b></span>
```cpp
_zwracany_typ_ operator==(const _typ1_&, const _typ2_&);

operator!=

<font size="2">w klasie</span> ```cpp _zwracany_typ_ operator!=(const _typ_&); ``` <font size="2">poza klasą</span> ```cpp _zwracany_typ_ operator!=(const _typ1_&, const _typ2_&); ```

operator<=

<font size="2">w klasie</span> ```cpp _zwracany_typ_ operator<=(const _typ_&); ``` <font size="2">poza klasą</span> ```cpp _zwracany_typ_ operator<=(const _typ1_&, const _typ2_&); ```

operator>=

<font size="2">w klasie</span> ```cpp _zwracany_typ_ operator>=(const _typ_&); ``` <font size="2">poza klasą</span> ```cpp _zwracany_typ_ operator>=(const _typ1_&, const _typ2_&); ```

operator&&

<font size="2">w klasie</span> ```cpp _zwracany_typ_ operator&&(const _typ_&); ``` <font size="2">poza klasą</span> ```cpp _zwracany_typ_ operator&&(const _typ1_&, const _typ2_&); ```

operator||

<font size="2">w klasie</span> ```cpp _zwracany_typ_ operator||(const _typ_&); ``` <font size="2">poza klasą</span> ```cpp _zwracany_typ_ operator||(const _typ1_&, const _typ2_&); ```

operator++

<font size="2">w klasie</span> ```cpp _zwracany_typ_ & operator++(); ``` <font size="2">poza klasą</span> ```cpp _zwracany_typ_ & operator++(_typ_&); ```

operator--

<font size="2">w klasie</span> ```cpp _zwracany_typ_ & operator--(); ``` <font size="2">poza klasą</span> ```cpp _zwracany_typ_ & operator--(_typ_&); ```

operator->

<font size="2">w klasie</span> ```cpp _zwracany_typ_ operator->(const _typ_&); ``` <font size="2">poza klasą</span> ```cpp _zwracany_typ_ operator->(const _typ1_&, const _typ2_&); ```

2 komentarzy

Chyba coś HTML się sypnął.

http://programmuj.blogspot.com/2011/01/przeciazanie-operatorow-w-jezyku-c.html Tutaj mamy prosty program z wykorzystaniem przeciążania ( przeładowywania) operatorów na przykładzie liczb zespolonych.