190 likes | 355 Views
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
E N D
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)
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; } }; Przeciążanie operatorów 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&); // deklaracje friend friend void wyswietl(punkt*); friend void wyswietl(punkt, char); friend void wyswietl(); };
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 classPunkt { staticint ile_punktow; float x, y; public: friendvoid wyswietl(kolo&); // deklaracja friend }; class Kolo { staticint ile_kol; float promien; punkt srodek; public: friendvoid 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 {…..};