Przeciążanie operatorów

Download Report

Transcript Przeciążanie operatorów

Przeciążanie operatorów
Na czym to polega ?
Krótko mówiąc, aby zmusić znaczki typu:
+,- itd. Aby robiły to co im każemy.
Kluczem do zrozumienia pisania operatorów jest
fakt abyśmy uświadomili sobie, że sami możemy
napisać funkcję "operatora dodawania", która będzie
wywoływana wtedy, gdy obok znaczka + pojawią się
argumenty wybranego przez nas typu. W tym momencie
zostanie przeładowany operator +.
Przeciążanie operatorów
Czym specjalnym wyróżnia sie taka funkcja?
NICZYM, poza nazwą, która musi przyjąć postać
operatorX, gdzie X oznacza wybrany przez nas
operator (np.: +,-,/,*). Poza tym jest to najzwyklejsza w
świecie funkcja, która o dziwo wcale nie musi
dodawać, odejmować itd. , może po prostu zagwizdać
naszym głośniczkiem w komputerze.
Kluczowym elementem jest to, że kiedy kolo obiektu
naszej klasy pojawi się znak + to funkcja zostanie
uruchomiona automatycznie!
obiekt1 + obiekt2
Można również wywołać ją jak zwykłą funkcję
operator+(obiekt1,obiekt2)
Przeciążanie operatorów
• Przykład:
• Rozważmy klasę która opisuje współrzędne punktów:
• class wsp{
• public:
•
float osX;
•
float osY;
•
•
•
•
•
•
• };
wsp operator+(wsp a,wsp b){
wsp suma;
suma.osX = a.osX + b.osX;
suma.osY = a.osX + b.osY;
return suma;
}
Ten przykład obrazuje w jaki sposób możemy przeciążyć
operator ‘’+’’ lub jakikolwiek inny.
Przeciążanie operatorów
Wiemy już zatem, że można przeładować operator +.
Nasuwa się pytanie kiedy to robić? Oczywiście w
sytuacji, gdy wobec obiektu danej klasy bardziej
naturalne wydaj się używać znaku + niż wywołanie
funkcji. Jest to moment, w którym powinniśmy
rozważyć możliwość przeładowania. Często się tak
dzieje wobec klas powiązanych z matematyka .Lecz
niekoniecznie, można przecież napisać:
ekran + okno
w celu ujrzenia na ekranie okienka.
Przeciążanie operatorów
Operator przypisania „=”
//Manipulowanie łańcuchami w c:
char napis[10];
napis = „Tekst”; //błąd
strcpy(napis, „Tekst”); //poprawnie
Przeciążanie operatorów
//Wersja w C++:
string napis;
napis = „Tekst”;
//poprawnie, gdyż w klasie string przeciążony
//został operator przypisania „=”
Przeciążanie operatorów jest techniką mającą głównie
na celu zwiększenie czytelności programu i ułatwienie
manipulowania obiektami.
Przykład przeciążenia operatora przypisania „=”
class Napis
{
private:
char bufor[100];
public:
Napis &operator=(const char* str);
void set(const char* str) {strcpy(bufor, str);}
const char *get() {return bufor;}
};
Napis &Napis::operator=(const char* str)
{
set(str);
return *this;
}
int main(int argc, char *argv[])
{
Napis napis;
napis = "PO to nasz ulubiony przedmiot:)";
cout << napis.get();
return 0;
}
Przykład przeciążenia operatora indeksowania „[ ]”
class Napis
{
private:
char bufor[100];
public:
Napis &operator=(const char* str);
char operator[](int i) {return bufor[i];}
void set(const char* str) {strcpy(bufor, str);}
const char *get() {return bufor;}
};
Napis &Napis::operator=(const char* str)
{
set(str);
return *this;
}
int main(int argc, char *argv[])
{
Napis napis;
napis = "PO to nasz ulubiony przedmiot:)";
for (int i = 0; i < 10; ++i)
cout << napis[i];
return 0;
}
Przeciążanie operatorów
Uwagi:
1.Przeładowywać można tylko powyżej podane operatory, nie
można wymyślać swoich.
2.Przeładowanie może nadać operatorowi dowolne znaczenie, nie
ma też ograniczeń co do wartości zwracanej przez operator(wyjatki
to: new i delete).
3.Nie można jednak zmienić priorytetu wykonywania tych
operatorów.
na przykład:
Wyrażenie
a+b*c
zawsze będzie wykonywane jako
a+(b*c)
a nie jako
(a+b)*c
Przeciążanie operatorów
4.Nie można zmienić argumentowości operatorów, czyli tego czy operator
jest jedno czy dwu argumentowy. Przykładowo: operator / (mnożenie)
zawsze będzie dwu argumentowy.
przykład:
element1 / element2 //dobrze!
element1 / element2/ //źle
Tak samo z operatorem ! (negacji) , jest jedno argumentowy.Nie możemy
wiec napisać
element1 ! element2 // źle
!element1
// dobrze
5. A oto lista operatorów które można przeciążać:
+ - * / % ^ & | - ! = < > += -= /= %= ^= &= |= <<
>> >>= <<= == != <= >= && || ++ -- , ->* -> () []
Przyjaźń. Funkcje i Klasy
zaprzyjaźnione.
Relacje przyjaźni są to stosunkowo niedawno dodane
właściwości C++.
Funkcje zaprzyjaźnione nie są jedynymi rodzajami form
zaprzyjaźnionych jakie może posiadać klasa. Sama
klasa również może być zaprzyjaźniona.
W takim przypadku każda metoda klasy
zaprzyjaźnionej ma dostęp do prywatnych
chronionych składowych klasy oryginalnej.
• To klasa definiuje jakie funkcje, metody lub inne klasy są w stosunku
do niej formami zaprzyjaźnionymi.
• Relacje „przyjaźnienia się” nie mogą być narzucane z zewnątrz do
prywatnych składowych klasy, w rzeczywistości nie naruszają one
żadnych idei programowania obiektowego.
• Głównym celem, „przyjaźnienia się” jest uelastycznienie interfejsu
publicznego.
• Po co chcieć zaprzyjaźnić jakąś klasę z inną ?
Popatrzmy na przykład.
•
Class Tv
{
public:
friend class Pilot ;
// Klasa Pilot ma dostęp do części prywatnej klasy TV
void onOff (void){
// instrukcje
}
void metoda(void ){
//instrukcje
}
private:
int stan ;
int glosnosc;
int wejscie;
int ustawKanal(int kanal) { }
}
• Class Pilot
{
private :
int tryb ;
public :
bool glosniej (Tv & t) { return t.glosniej(); }
bool ciszej (Tv & t) { return t.ciszej();}
void onOff (Tv & t) { t.onOff(); }
void ustaw() {ustawKanal(int kanal);}
};
Uwaga :
Każda z metod klasy Pilot pobiera jako argument referencje do
obiektu Tv - jest to odzwierciedlenie faktu skojarzenia pilota z
określonym telewizorem.
•
Funkcje zaprzyjaźnione
Niezależne funkcje zaprzyjaźnione mają dostęp do składowych
prywatnych i chronionych klasy. Sama funkcja nie nabywa własności
składowej klasy.
class Punkt
{
static int ile_punktow;
float x, y;
public:
friend void wyswietl(punkt&);
friend
friend void wyswietl(punkt*);
friend void wyswietl(punkt, char);
friend void wyswietl();
};
// deklaracje
void wyswietl(punkt& p)
{
cout<<”Przekazanie przez referencje punktu o “;
cout <<”wspolrzednych: “ << p.y << “ “<< p.y << “\n”; getch();
}
void wyswietl(punkt *p)
{
ciało funkcji bez hermetyzacji obiektu p
}
void wyswietl(punkt p, char)
{
ciało funkcji bez hermetyzacji obiektu p
}
void wyswietl()
{
cout <<"Liczba punktow: " << punkt::ile_punktow << "\n";
}
• Funkcje zaprzyjaźnione z wieloma klasami
Niezależna funkcja zaprzyjaźniona z kilkoma klasami ma dostęp do
wszystkich składowych prywatnych i chronionych tych klas. Sama
funkcja nie nabywa jednak własności składowej tych klas. Deklaracja
zaprzyjaźnienia powinna wystąpić w każdej z tych klas, natomiast
definicja tylko raz .
class Kolo; // niezbędna deklaracja klasy kolo ze względu na deklarację friend
class Punkt
{ static int ile_punktow;
float x, y;
public:
friend void wyswietl(kolo&);
// deklaracja friend
};
class Kolo
{
static int ile_kol;
float promien;
punkt srodek;
public:
friend void wyswietl(kolo&);};
// deklaracja friend
Dzięki zaprzyjaźnieniu do wyświetlenia utworzono jedną funkcje
wyświetl. W ciele funkcji są dostępne wszystkie składowe klas kolo
i punkt
• Język C++ dopuszcza również możliwość
wskazania poszczególnych składowych
klasy, które mają być zaprzyjaźnione lecz,
jest to trochę kłopotliwe.
• W takiej sytuacji powinniśmy pamiętać o
zachowaniu odpowiedniej kolejności w
jakiej pojawiają się różne deklaracje i
definicje.
• Przykład :
Chcąc zaprzyjaźnić metodę Pilot :: ustaw kanał () z klasą Tv należy
zadeklarować ją z przedrostkiem friend i umieścić w sekcji
deklaracyjnej klasy Tv
class Tv
{
friend Pilot :: ustaw kanał (Tv &t )
}
• Jednakże aby kompilator mógł przetworzyć taką instrukcję, wcześniej
musi do definicji klasy Pilot. W przeciwnym wypadku nie będzie
wiedział, że Pilot jest klasą a ustawkanal() metodą tej klasy. Jednak
metody klasy Pilot odwołują się do klasy Tv.
• W tym celu przed definicją klasy Pilot trzeba umieścić „zapowiedź”
klasy Tv
class Tv ; // deklaracja zapowiadająca
class Pilot {….};
class Tv {…..};