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> ```cppzwracany_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> ```cppzwracany_typ operator==(const typ&);
<font size="2"><b>poza klasą</b></span>
```cpp
_zwracany_typ_ operator==(const _typ1_&, const _typ2_&);
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.