Kategorie materiałów Informatyka

Przedmiot: C++ Dla Zaawansowanych Wróć do kategorii

LEKCJA 34. Overloading operatorów

plik Pobierz LEKCJA 34. Overloading operatorow.txt

LEKCJA 34 OVERLOADING OPERATORÓW. ________________________________________________________________ Podczas tej lekcji poznasz możliwości dostosowania operatorów  C++ do własnego "widzimisię" i do potrzeb własnych obiektów.  ________________________________________________________________  Niemal od początku niniejszej książki korzystamy z operatorów  poddanych overloadingowi. Są to operatory << i >> , które  pierwotnie wykonywały bitowe przesunięcie w lewo i w prawo.  Owerloading tych operatorów "załatwił" za nas producent  (Borland, Microsoft, czy inny). Jak widzisz, nie powoduje to w  dalszym użytkowaniu tych operatorów żadnych zauważalnych  komplikacji, a często ułatwia tworzenie programów. Zwróć uwagę,  że overloading operatorów (jak i definicje klas) może znajdować  się w dołączonych plikach nagłówkowych i po jednorazowym  wykonaniu może być "niewidoczny" dla programistów tworzących  programy aplikacyjne.  Jeśli projektujemy (definiujemy) nową klasę, dodajemy do C++  nowy, lecz pełnoprawny typ danych. Autorzy C++ nie byli w stanie  przewidzieć jakie klasy i jakie obiekty mogą wymyślić kolejne  pokolenia programistów w ramach swojej radosnej twórczości.  Wprowadzili zatem do C++ jasne i jednoznaczne algorytmy  postępowania z typami "typowymi". C++ doskonale wie jak dodawać,  mnożyć, czy odejmować np. liczby int, long, float itp., nie wie  jednak jak dodać do siebie obiekty klas CString (CString = Class  String = klasa "łańcuch znaków"), TOdcinek (to taki kawałek  prostej) itp.. A przecież miło byłoby, gdyby rozbudować   działanie operatorów tak, by było możliwe ich typowe  zastosowanie w stosunku do naszych własnych, "nietypowych"  obiektów:   int x, y;     int z = x + y;      //To operator + załatwia sam float x, y;   float z = x + y;   Zanim jednak stanie się możliwe postępowanie takie:  class CString x, y, z;     z = x + y;   class Nasza_Klasa obiekt1, obiekt2, obiekt3;    obiekt3 = obiekt1 + obiekt2;   itp., itd. ...  musimy "uzupełnić" C++ i "wyjaśnić" operatorom, co właściwie ma  w praktyce oznaczać operacja   obiekt1 = obiekt2 + obiekt3; .  Jest wyczuwalne intuicyjnie, że działanie operatorów w stosunku  do różnych obiektów może być różne. Dla przykładu - wiesz  zapewne, że inaczej wygląda algorytm mnożenia liczb zespolonych,  a inaczej liczb całkowitych rzeczywistych. Dlatego też wykonanie  operacji mnożenia wymaga od operatora * podjęcia różnych  działań:   class Liczba_zespolona x, y, z;         z = x * y;   int x, y, z;                            z = x * y;   Czasem może się zdarzyć, że dla dwu różnych klas działanie  jakiegoś operatora jest identyczne, częściej jednak (i tak  należy się spodziewać) działanie operatora dla każdej klasy  będzie odrębne i unikalne.   Pójdźmy w tym rozumowaniu o krok dalej. Skoro rozszerzenie  obszaru zastosowań jakiegoś operatora na obiekty nowej  (nieznanej wcześniej klasy) wymaga zdefiniowania nowego  algorytmu działania operatora, C++ będzie potrzebował do tego  celu specjalnych środków, które powinny być łatwo rozpoznawalne.  Do opisu algorytmów służą generalnie w C++ funkcje i tu Autorzy  nie wprowadzili wyjątku. Zastrzegli jednak dla tych specjalnych  funkcji specjalną nazwę:              operator ...();   I tak funkcja precyzująca nowy algorytm dodawania (nowy sposób  działania operatora + ) będzie się nazywać:   operator+();   a np. funkcja określająca nowy algorytm mnożenia (nowy sposób  działania operatora * ) będzie się nazywać:   operator*();   Spróbujmy zastosować taką filozofię w praktyce programowania.   [!!!] NIESTETY NIE WSZYSTKIE OPERATORY MOŻNA ROZBUDOWAĆ. ________________________________________________________________ Są w C++ operatory, których nie możemy poddać overloadingowi. Są  to:   .   ::   .*   ?:   .   operator kropki umożliwia dostęp do pól struktur i obiektów;  ::  operator "widoczności-przesłaniania" (ang. scope);  .*  wskazanie członka klasy (ang. pointer-to-member);  ?:  operator warunkowy.  ________________________________________________________________  Wszystkie pozostałe operatory możemy poddać overloadingowi i  przypisywać im potrzebne nam działanie.  OVERLOADING OPERATORA [+] (DWUARGUMENTOWEGO).  Zaczniemy od operatora + należącego do grupy "dwuargumentowych  operatorów arytmetycznych" (ang. binary arithmetic operator).  Zwracamy tu już na początku rozważań uwagę na przynależność  operatora do określonej grupy, ponieważ overloading różnych  opertorów należących do tej samej grupy przebiega podobnie.  Ponieważ znak + może być także operatorem jednoargumentowym  (ang. unary plus, o czym za chwilę), podkreślamy, że tym razem  chodzi o plus jako operator dodawania. Overloading operatora  przeprowadzimy w stosunku do obiektów prostej, znanej Ci już z  poprzednich przykładów klasy Data, którą (w celu upodobnienia  się do maniery stosowanej w Windows i bibliotekach klas)  nazwiemy tym razem CData. "Namówimy" operator + do  przeprowadzenia operacji na obiektach (dokładniej na polach  obiektów):   CData nowadata = staradata + 7;       // W tydzien pozniej   Operator + musi oczywiście "wiedzieć", na którym polu obiekty  klasy CData przechowują liczbę dni i jak związane są (logicznie)  pola obiektu dz, mc, rok. Jest rzeczą zrozumiałą, że samo  dodanie dni do pola dz może nie wystarczyć, ponieważ data  37.11.93 jest niedopuszczalna.   Jeśli staradata jest obiektem klasy CData z zawartymi wewnątrz  danymi, to w wyniku działania "nowego" operatora + powinien  powstać obiekt nowadata klasy CData, którego pola zostaną w  sensowny sposób powiększone o dodaną liczbę dni. Rozważ  działanie programu (najlepiej skompiluj i uruchom).   [P120.CPP]   /*  Overloading operatora dwuargumentowego +     */   # include <iostream.h>   class CData  {    int dz, mc, rok;  public:    CData() {}         //Konstruktor domyslny (pusty)   CData(int d, int m, int y) { mc = m; dz = d; rok = y; }    void Pokazuj() { cout << dz << '.' << mc << '.' << rok; }    CData operator+(int);       //TU! overloading operatora +  };    static int TAB[] = {31,28,31,30,31,30,31,31,30,31,30,31};   /* Definicja funkcji operatorowej: ------------------------ */  CData CData::operator+(int n)  {    CData kopia_obiektu = *this;    n += kopia_obiektu.dz;    while (n > TAB[kopia_obiektu.mc-1])     {        n -= TAB[kopia_obiektu.mc-1];        if (++kopia_obiektu.mc == 13)          { kopia_obiektu.mc = 1; kopia_obiektu.rok++; }      }    kopia_obiektu.dz = n;    return (kopia_obiektu);  }    main()  {    CData staradata(31, 1, 94);    //Kostruktor z argumentami   CData nowadata;                //Pusty konstruktor   cout << "\n Stara data: ";    staradata.Pokazuj();    cout << "\n Podaj ile minelo dni --> ";    int n;    cin >> n;   nowadata = staradata + n;   cout << "\n Jest zatem -->  ";   nowadata.Pokazuj();    return 0; }    Do tej pory do danych prywatnych obiektu mogliśmy sięgnąć  wyłącznie przy pomocy zdefiniowanej wewnątrz klasy  funkcji-metody. Metodą umożliwiającą nam dostęp do prywatnych  danych obiektu jest tu zadeklarowana wewnątrz klasy (a więc  mająca "status prawny" metody) funkcja operatorowa. Przyjrzyjmy  się tej funkcji dokładniej:   CData CData::operator+(int n)  {    CData kopia_obiektu = *this;  ...    return (kopia_obiektu);  }  Funkcja   * została zdefiniowana dla obiektów klasy CData (z innymi  postępować nie potrafi);  Jeśli operator + zostanie umieszczony pomiędzy obiektem klasy  CData, a liczbą typu int:                               .... staradata + n;  * funkcja pobiera liczbę n jako argument (jawnie);  * funkcja pobiera obiekt klasy CData jako swój drugi argument  (niejawnie, dzięki pointerowi this); * funkcja zwróci obiekt klasy CData (ze zmodyfikowanym polem);   Nowy obiekt zwrócony przez funkcję zostanie przypisany   nowadata = ... ;      // <-- return(kopia_obiektu);   W prawym polu operatora (operator jest dwuargumentowy, ma więc  swoje lewe i prawe pole) może pojawić także stała. Operacja:   nowadata = staradata + 14;   zostanie wykonana poprawnie.   Ale to nie wszystko. Jeśli wystąpi układ odwrotny - np.:   nowadata = 14 + staradata;   nasz operator "zgłupieje". Doszedłszy do operatora + C++ "nie  będzie jeszcze wiedział" (analizuje wyrażenia arytmetyczne od  lewej do prawej), KTÓRY obiekt wystąpi za chwilę. Jedno jest  pewne, nie zawsze musi być to "własny" obiekt funkcji, do  którego mamy pointer this. Aby uzyskać jednoznaczność sytuacji,  funkcja operatorowa powinna tu w jawny sposób pobierać przed  zadziałaniem dwa argumenty:   CData operator+(int n, CData obiekt);   aby działanie:   CData obiekt_wynik;     obiekt_wynik = n + obiekt;   stało się wykonalne. Pojawia się tu wszakże pewien problem.  Wskaźnik this wskazuje własny obiekt funkcji-metody, a tym razem  funkcja potrzebuje dostępu nie do pola własnego obiektu, lecz do  pola "obcego" obiektu przekazanego jej jako argument. Ale w C++  możemy:   * zdefiniować dwie (i więcej) funkcji o tej samej nazwie (każda  na inną ewentualność);  * możemy nadać funkcji status friend (wtedy nie będąc metodą też  uzyska dostęp do danych obiektu).   Definicja naszej klasy CData zawierająca deklaracje dwu funkcji  operatorowych operator+() różniących się zastosowaniem i (po  czym rozpozna je C++) liczbą argumentów, będzie wyglądać tak:   class CData  {    int dz, mc, rok;  public:    CData() {}    CData(int d, int m, int y) { mc = m; dz = d; rok = y; }    void Pokazuj() { cout << dz << '.' << mc << '.' << rok; }  /* Dwie funkcje operatorowe: ------------------------------ */   CData operator+(int);    friend CData operator+(int, CData&); };   Zastosowaliśmy zamiast kopii obiektu bezpośrednio przekazywanej  funkcji - referencję do obiektu klasy CData - CData&. Klasa  zawiera:  * prywatne dane;  * dwa konstruktory;  * własną metodę - funkcję operatorową operator+();  * deklarację zaprzyjaźnionej z klasą funkcji kategorii friend  (choć jest to funkcja o tej samej nazwie, jej status i  uprawnienia są nieco inne).   [!!!] NIE WSZYSTKO, CO WEWNĄTRZ JEST METODĄ.  ________________________________________________________________ Nawet, jeśli wewnątrz definicji klasy zdefiniujemy w pełni  funkcję (nadając jej status inline), nie stanie się ona metodą!  Słowo kluczowe friend określa status funkcji jednoznacznie, bez  względu na to, w którym miejscu w tekście programu umieścimy  definicję ciała funkcji.  ________________________________________________________________   W zasadzie ciało funkcji jest na tyle proste (wymagamy od niej  tylko zwrotu obiektu ze zmodyfikowanym polem danych), że możemy  skorzystać z rozbudowanego wcześniej operatora + i całe ciało  zdefiniować tak:   class CData  {    int dz, mc, rok;  public:   ...   CData operator+(int);    friend CData operator+(int n, CData& x) { return (x + n); } };   Jeśli w operacji dodawania argumenty zastosujemy we  wcześniejszej kolejności:     return (obiekt + liczba);   to zostanie tu wykorzystany operator + rozbudowany poprzednio  przez metodę  CData::operator+(int). Program w całości może  zatem wyglądać tak:   [P121.CPP]   # include "iostream.h"   class CData  {    int dz, mc, rok;  public:    CData() {}    CData(int d, int m, int y) { mc = m; dz = d; rok = y; }    void Pokazuj() { cout << dz << '.' << mc << '.' << rok; }    CData operator+(int);    friend CData operator+(int n, CData& x) { return (x + n); } };    static int TAB[] = {31,28,31,30,31,30,31,31,30,31,30,31};   CData CData::operator+(int n)  {    CData kopia_obiektu = *this;    n += kopia_obiektu.dz;    while (n > TAB[kopia_obiektu.mc-1])     {        n -= TAB[kopia_obiektu.mc-1];        if (++kopia_obiektu.mc == 13)          { kopia_obiektu.mc = 1; kopia_obiektu.rok++; }      }    kopia_obiektu.dz = n;    return (kopia_obiektu);  }    main()  {    CData staradata(31, 1, 94);    //Kostruktor z argumentami   CData nowadata, jeszczejednadata;    cout << "\n Stara data: ";    staradata.Pokazuj();    cout << "\n Podaj ile minelo dni --> ";    int n;    cin >> n;   nowadata = staradata + n;   cout << "\n Jest zatem -->  ";   nowadata.Pokazuj();    cout << "\n Testuje nowy operator:  ";   jeszczejednadata = (1+n) + staradata;    jeszczejednadata.Pokazuj();   return 0; }   Operator + w obu sytuacjach działa poprawnie. Być może wpadłeś  na pomysł, że operator - (minus) też mamy już z głowy. Niby tak,  ale tylko w takim zakresie, w jakim nasza funkcja operatorowa  poprawnie będzie obsługiwać ujemne liczby dni. Jeśli zechcesz  podać ujemną liczbę dni (zmuszając funkcję do odejmowania  zamiast dodawania), twój dialog z programem będzie wyglądał np.  tak:    C:\>program  Stara data: 31.1.94   Podaj ile minelo dni -->  -10  Jest zatem -->  21.1.94   Testuje nowy operator:  22.1.94   lub tak:   C:\>program   Stara data: 31.1.94   Podaj ile minelo dni -->  -150  Jest zatem -->  -119.1.94   Testuje nowy operator:  -118.1.94   Funkcja operatorowa została napisana w taki sposób, że po  przekroczeniu wartości -31 program będzie wypisywał bzdury. Jako  zadanie domowe - spróbuj zmodyfikować algorytm w taki sposób, by  rozszerzyć zakres poprawnych wartości.   [!!!] Możesz dodawać obiekty minusem. ________________________________________________________________ * Należy tu zwrócić uwagę, że dodawanie obiektów może wykonywać  nie tylko i nie koniecznie operator + . Jeśli zechcesz, możesz  do tego celu zastosować dowolnie wybrany operator (np. -, *  itp.). W celu ułatwienia zrozumienia zapisu (i tylko dlatego)  większość programistów rozbudowuje działanie operatorów zgodnie  z ich pierwotnym zastosowaniem.  * DOWOLNOŚĆ, ALE NIE PEŁNA!  O tyle, o ile działanie operatora może być zmienione, to ilość  argumentów potrzebnych operatorowi pozostaje w C++ "sztywna"  (patrz przykład z n!). ________________________________________________________________  W bardzo podobny sposób możesz rozbudowywać inne arytmetyczne  operatory dwuargumentow ...

Ciąg dalszy w pliku do pobrania.

Wkuwanko.pl jako podmiot świadczący usługę hostingu materiałów edukacyjnych nie ponosi odpowiedzialności za ich zawartość.

Aby zgłosić naruszenie prawa autorskiego napisz do nas.

ikona Pobierz ten dokument

Wróć do kategorii

wkuwanko.pl

Wasze komentarze: dodaj komentarz

  • Nie ma jeszcze komentarzy do tego materiału.

Materiały w kategorii C++ Dla Zaawansowanych [22]

  • podgląd pobierz opis LEKCJA 25. Przykład obiektu
  • podgląd pobierz opis LEKCJA 27. Dziedziczenie
  • podgląd pobierz opis LEKCJA 28. Dziedziczenie złożone
  • podgląd pobierz opis LEKCJA 29. Funkcje i overloading
  • podgląd pobierz opis LEKCJA 30. Wymiana danych między obiektami
  • podgląd pobierz opis LEKCJA 31. Przekazanie obiektów jako argumentów do funkcji
  • podgląd pobierz opis LEKCJA 33. Wskaźniki do obiektów
  • podgląd pobierz opis LEKCJA 34. Overloading operatorów
  • podgląd pobierz opis LEKCJA 35. O zastosowaniu dziedziczenia
  • podgląd pobierz opis LEKCJA 36. Funkcje wirtualne i klasy abstrakcyjne
  • podgląd pobierz opis LEKCJA 37. Jak komputer dysponuje swoimi zasobami w środowisku tekstowym (DOS)
  • podgląd pobierz opis LEKCJA 38. Programowanie dla Windows
  • podgląd pobierz opis LEKCJA 39. Wykorzystanie standardowych zasobów Windows
  • podgląd pobierz opis LEKCJA 40. Struktura programu proceduralno-zdarzeniowego
  • podgląd pobierz opis LEKCJA 41. Jak tworzy się aplikację dla Windows
  • podgląd pobierz opis LEKCJA 42. Kompilaroty specjalnie dla Windows
  • podgląd pobierz opis LEKCJA 43. Elementy sterujące i zarządzanie programem
  • podgląd pobierz opis LEKCJA 44. Okna dialogowe
  • podgląd pobierz opis LEKCJA 45. Dołączanie zasobów - menu i okienka dialogowe
  • podgląd pobierz opis LEKCJA 46. Programy obiektowo-zdarzeniowe
[ Misja ] [ Regulamin ] [ Kontakt ] [ Reklama ]   © wkuwanko.pl 2008-2019 właściciel serwisu SZLIFF

Partnerzy: matzoo.pl matmag.pl batmat.pl pisupisu.pl Matematyka