Różnice

Różnice między wybraną wersją a wersją aktualną.

Odnośnik do tego porównania

pl:dydaktyka:jimp2:2017:labs:operatory [2017/04/04 02:07]
mwp [Konwersja typów]
pl:dydaktyka:jimp2:2017:labs:operatory [2019/06/27 15:50]
Linia 1: Linia 1:
-====== Przeciążanie operatorów ====== 
-=====Wykorzystanie przeciążania operatorów===== 
- 
-Pisząc program w C%%++%% można korzystać zarówno z typów wbudowanych jak i z typów zdefiniowanych przez siebie (jako nowe klasy). ​ 
- 
-Wszystkie typy wbudowane mają zdefiniowane operatory porównania i przypisania,​ inkrementacji czy dekrementacji;​ tablice, wskaźniki czy referencje również posiadają zestaw operatorów ułatwiających (czy nieraz wręcz umożliwiających korzystanie z ich własności). ​ 
- 
-Dla typów zdefiniowanych przez programistę można zdefiniować (przeciążyć) operatory istniejące w języku C%%++%% dla danego typu, dzięki czemu stosowanie ich w kontekście tych typów ma sens. Jednym z typowych przykładów przeciążania operatorów jest przeładowanie operatora wstawiania i pobierania danych ze strumienia:<​file cpp Point.h>//​ Deklaracja znajduje się w pliku Point.h 
-#ifndef POINT_H 
-#define POINT_H 
-#include <​iostream>​ 
- 
-class Point { 
-  public: 
-    ​ 
-    ... 
-    void SetX(double x); 
-    void SetY(double y); 
-    ​ 
-    //​opcjonalna definicja pozwalająca na uzyskanie 
-    //dostępu do prywatnych pól z wewnątrz definicji 
-    //funkcji zadeklarowanej poniżej 
-//    friend std::​istream&​ operator>>​(std::​istream &, Punkt&​);​ 
-    ... 
-    ​ 
-  private: 
-    double x_, y_; 
-}; 
- 
-//​właściwa deklaracja, przeciążająca 
-//operator >> dla strumienia wejściowego 
-//i klasy punkt 
-std::​istream&​ operator>>​(std::​istream &is, Point& point); 
-#endif 
-</​file>​ 
- 
-I definicja w pliku Point.cpp 
-<file cpp Point.cpp>​ 
-//Definicja w Point.cpp 
-#include "​Point.h"​ 
-#include <​iomanip>​ 
-#include <​iostream>​ 
- 
-using ::​std::​istream;​ 
- 
-//Helper functions: 
-void CheckNextChar(char c, istream *is) { 
-  int next_char = is.peek(); 
-  if (next_char != c) { 
-    throw runtime_error("​invalid character"​);​ 
-  } 
-  is.ignore();​ 
-} 
- 
-void IgnoreWhitespace(istream *is) { 
-  is >> ws; 
-} 
- 
-double ReadNumber(istream *is) { 
-  double d; 
-  is >> d; 
-  return d; 
-} 
- 
- 
-// Właściwa definicja, obydwa argumenty funkcji nie 
-//są zadeklarowane jako const, bo obydwa są modyfikowane 
-//wewnątrz funkcji (STL nie używa naszej konwencji z przekazywaniem ​ 
-//przez wskaźnik) 
-istream&​ operator>>​(istream & input, Punkt& p){ 
-    CheckNextChar('​(',​ &​input);​ 
-    p.SetX(ReadNumber(&​input));​ 
-    CheckNextChar(',',​ &​input);​ 
-    IgnoreWhitespace(&​input);​ 
-    p.SetY(ReadNumber(&​input));​ 
-    CheckNextChar('​)',​ &​input);​ 
-    return input; ​     // Umożliwia cin >> a >> b >> c; 
-} 
-</​file>​ 
- 
-Powyższy przykład umożliwia wczytanie punktu podanego w formacie //(12, 22)//. Należy zwrócić uwagę, że operator ">>"​ nie może być metodą klasy a funkcją zaprzyjaźnioną jeśli chcemy wywołać go jako <code cpp>cin >> punkt;</​code> ​ Jeśli operator zadeklarowany byłby jako metoda klasy trzeba by go wywoływać na przykład tak:<​code cpp>​punkt >> cin</​code>​ 
- 
-===== Jak to działa ===== 
-Operatory to "​zwyczajne"​ funkcje w języku C%%++%%. Kompilator zamienia każde pojawienie się operatora na wywołanie odpowiedniej funkcji.  ​ 
- 
-Zakładając że operatory + oraz * są składowymi klas, tak będzie wyglądało użycie tych operatorów dla kompilatora:​ 
-<code cpp> 
-Matrix a, b; 
-a + b; // Dla kompilatora oznacza to a.operator+(b);​ (argument a zostanie przesłany jako this do funkcji) 
-a * b; // dla kompilatora oznacza to a.operator*(b);​ (j.w.) 
-</​code>​ 
- 
-Można także zdefiniować niektóre operatory jako funkcje nieskładowe (jak w przykładzie z poprzedniej sekcji). Zakłądjąc że operatory + i * są zadeklarowane jako funkcje zaprzyjaźnione,​ tak będzie wyglądało użycie tych operatorów dla kompilatora:​ 
-<code cpp> 
-Matrix a, b; 
-a + b; // Dla kompilatora oznacza to operator+(a,​b);​ 
-a * b; // dla kompilatora oznacza to operator*(a,​b);​ 
-cin >> a; // dla kompilatora oznacza to operator>>​(cin,​ a) 
-cin >> a >> b; // dla kompilatora oznacza to operator>>​( operator>>​(cin,​ a), b) 
-//stąd też wynika, że wartość zwracana przez funkcję operator>>​ musi być taka sama jak pierwszy argument tej funkcji 
-</​code>​ 
- 
-===== Operatory jedno- i dwuargumentowe===== 
-Operatory **jednoargumentowe** można przeciążyć za pomocą niestatycznej funkcji składowej nie pobierającej żadnych argumentów (jeden niejawny argument jest przekazywany jako **this**) lub za pomocą funkcji nie będącej składową klasy, pobierającą jeden argument, który jest obiektem (przekazywanie przez wartość) lub referencją do obiektu tej klasy. **Wyjątkiem** jest operator rzutowania, który NIE może być zadeklarowany jako funkcja nieskładowa. 
- 
-Funkcje nieskładowe mogą być dodatkowo deklarowane jako zaprzyjaźnione z klasą dla której są zdefiniowane wtedy mają dostęp do prywatnych składników klasy. ​ 
- 
-Załóżmy, że klasa Text jest naszą wersją klasy //string//, przechowującej napisy i umożliwiającą manipulowanie nimi. Chcąc zdefiniować na przykład operator **!** (logiczne zaprzeczenie) dla klasy Napis, który będzie zwracał //true// jeśli obiekt będzie zawierał pusty napis, a w każdym innym przypadku //true//, możemy to zrobić na da sposoby: 
- 
-<code cpp> 
-class Text{ 
-  public: 
-    // składowa klasy 
-    bool operator!() const; 
-    // LUB (opocjonalnie) funkcja nieskładowa zaprzyjaźniona 
-//    friend bool operator!()(const Text&) 
-    ... 
-}; 
- 
-  //LUB funkcja nieskładowa,​ która dodatkowo może być zaprzyjaźniona ​ 
-//bool operator!()(const Text &text) 
-</​code>​ 
- 
-Operatory **dwuargumentowe** można przeciążyć za pomocą niestatycznej funkcji składowej klasy pobierającej jeden argument (pierwszy argument jest przekazywany niejawnie jako **this**) lub za pomocą funkcji nie będącej składową klasy, pobierającą dwa argumenty z których jeden jest obiektem lub referencją do obiektu tej klasy. **Wyjątkami** są operatory przypisania,​ **[]** oraz **->**, które NIE mogą być zadeklarowane jako funkcje zaprzyjaźnione. 
- 
-<code cpp> 
-class Text{ 
-  public: 
-    // składowa klasy (pomysl dlaczego zwracamy const) 
-    const Text operator+=(const Text& txt); 
-    // LUB funkcja zaprzyjaźniona 
-    friend const Text operator+=()(Text&​ lhs, const Text& rhs); 
-    ... 
-}; 
-const Text operator+=()(Text&​ lhs, const Text& rhs); 
-</​code>​ 
- 
-=====Konwersja typów===== 
-Specyficznym rodzajem operatorów są tzw. operatory rzutowania. Wykorzystywane one są do konwersji typów, czy inaczej mówiąc do rzutowania. W prosty sposób można rzutować pomiędzy typami wbudowanymi:​ 
-<code cpp> 
-double number = 12.34; 
-int integer_value = static_cast<​int>​(number);​ 
-</​code>​ 
-Aby możliwe było rzutowanie pomiędzy typami zdefiniowanymi przez programistę,​ konieczna jest definicja operatorów realizujących taką funkcjonalność. 
-Operatory rzutowania NIE mogą być zadeklarowane jako funkcje zaprzyjaźnione - muszą być składowymi klasy. Dodatkowo niczego nie zwracają: 
-<code cpp> 
-//Complex.h 
-class Complex{ 
-  public: 
-    operator Matrix() const; 
-    ... 
-}; 
-//​Complex.cpp 
-Complex::​operator Matrix() const { 
-   ​return Matrix {{*this}}; 
-} 
-</​code>​ 
- 
-Dzięki powyższej deklaracji i odpowiedniej definicji możliwe będzie następujące wywołanie:<​code cpp> 
-Complex c("​4i5"​);​ 
-//Powinien utworzyć macierz 1x1 z wartością 4i5 w jedynej komórce 
-Matrix m = (Matrix)c; 
-</​code>​ 
- 
-===== Operatory inkrementacji i dekrementacji===== 
-Operatory **%%++%%** i **- -** mogą również być przeciążane. Mogą one być jednak stosowane zarówno w wersji prefiksowej i postfiksowej. ​ 
- 
-==== Operator prefiksowy==== 
-Chcąc przeładować operator inkrementacji dla klasy //Complex// tak aby można było wykonać następującą operację:<​code cpp> 
-Complex c(1); 
-++c; 
-//​kompilator tak anprawdę wywołuje c.operator++() 
-</​code>​ 
-należy zadeklarować operator **%%++%%** w następujcy sposób: 
-<code cpp> 
-class Complex{ 
-  public: 
-    Complex &​operator++();​ 
-}; 
-</​code>​ 
- 
-==== Operator postfiksowy==== 
- 
-Aby możliwe było wywołanie inkrementacji w notacji postfiksowej:​ 
-<code cpp> 
-Complex c(1); 
-c++; 
-//​kompilator tak naprawdę wywołuje ​ c.operator++(0) 
-</​code>​ 
-konieczne jest zadeklarowanie operatora **%%++%%** w następujący sposób: 
-<code cpp> 
-class Complex{ 
-  public: 
-    // parametr "​int"​ jest "​dowolnym argumentem"​ który jest wykorzystywany 
-    // przez kompilator do odróżnienia operatora postfiksowego od  
-    // operatora prefiksowego. Podczas wywołania c++ 
-    // kompilator wywoła tak naprawdę c.operator++(0) 
-    Complex operator(int);​ 
-}; 
-</​code>​ 
- 
-=====Tabela operatorów===== 
-^Operatory, które można przeciążać^^^^^^^ 
-|+|-|*|/​|%|<​nowiki>​^</​nowiki>​|&​| 
-|<​nowiki>​|</​nowiki>​|~|!|=|<​|>​|+=| 
-|-=|*=|/​=|<​nowiki>​%=</​nowiki>​|<​nowiki>​^=</​nowiki>​|&​=|<​nowiki>​|=</​nowiki>​| 
-|<<​|>>​|>>​=|<<​=|==|!=|<​nowiki><​=</​nowiki>​| 
-|>​=|&&​|<​nowiki>​||</​nowiki>​|%%++%%|<​nowiki>​--</​nowiki>​|->​*|,​| 
-|->​|[]|()|new|delete|new[]|delete[]| 
- 
- 
-^Operatory, których nie można przeciążać^^^^^ 
-|.|.*|::​|?:​|sizeof| 
- 
-======Ćwiczenia====== 
-  - [1 plus] Przetestować operator wczytywania dla klasy Punkt i dopisz operator wypisania ("<<"​). Sprawdź w jakich przypadkach ten operator się wywołuje. 
-  - Do klasy tablica dynamiczna DTab (z laboratorium [[.:​klasy1|Klasy i obiekty I]]):  
-    - [1 plus] dopisz operator '​[]'​ pozwalający na pobranie danego elementu tablicy za pomocą konstrukcji <code cpp>DTab d(10); 
-cout << d[5] << endl;</​code>​ 
-    - [1 plus] Jak zrobić żeby dało się ustawić element tablicy za pomocą tego operatora, a nie tylko go pobrać?:<​code cpp>​tab[2] = 7; //ustawia 2 element tablicy na wartość 7</​code>​ 
-    - [1 plus] dopisz operator przypisania ( = ) 
-    - [1 plus] dopisz operatory << i >>. Przyjmij format pobierania tablicy jako <​code>​[1,​2,​3,​4]</​code>​ 
-    - [1 plus] dopisz operator <​nowiki>​++</​nowiki>​ umożliwiający rozszerzenie tablicy o 1 
-  - [1 plus] Wykorzystując klasę //Matrix// napisz dla klasy //Complex// operator rzutowania:<​code cpp>​Complex c("​5i3"​);​ 
-Matrix nowa = (Matrix)c; 
-</​code>​ 
-  - **[5 punktów] Napisz klasę Mapa, która będzie zawierać listę obiektów typu Para. Klasa Para powinna posiadać dwa pola jedno typu //string// a drugie typu //​integer//​. Klasa Mapa powinna mieć konstruktor przyjmujący jako parametr ścieżkę do pliku tekstowego. W konstruktorze powinno nastąpić odczytanie pliku i zbudowanie indeksu wyrazów, tak aby każdy wyraz z pliku miał odpowiadający sobie obiekt klasy Para w liście. Pole typu //string// wewnątrz obiektów Para powinno odpowiadać danemu słowu, natomiast pole typu //integer// ilości powtórzeń tego słowa we wczytanym pliku. Plik jest __dowolnym plikiem tekstowym!__ Analizując go, ignorujemy znaki interpunkcyjne,​ spacje tabulatory, etc. i wczytujemy tylko słowa. ** 
-    * Przeładuj operator klasy Mapa **[]** tak aby możliwe było poniższe wywołanie:<​code cpp>Mapa m("​myfile,​txt"​);​ 
-// w zmiennej ilosc powinna znaleźć się ilość powtórzeń 
-// słowa "​programowanie"​ w pliku "​myfile.txt"​ 
-int ilosc = m["​programowanie"​];</​code> ​ 
-    * Przeładuj operator **%%<<​%%** dla klasy Mapa, aby możliwe było wyświetlenie raportu o ilości słów i ich liczebności w danym pliku. Dane wyświetlane powinny być posortowane malejąco. Do tego celu wykorzystaj metodę //​[[http://​www.cplusplus.com/​reference/​list/​list/​sort/​|sort]]//​ klasy //list// - wykorzystaj listę z biblioteki standardowej!. 
-    * Przeładuj operatory porównania (//<,>,​==//​) i przypisania (//=//) dla klasy Para (porównywanie względem liczebności). 
-    * Przeładuj operator **%%++%%** dla klasy Para, tak aby można było szybko inkrementować liczebność danego słowa podczas budowania mapy. 
  
pl/dydaktyka/jimp2/2017/labs/operatory.txt · ostatnio zmienione: 2019/06/27 15:50 (edycja zewnętrzna)
www.chimeric.de Valid CSS Driven by DokuWiki do yourself a favour and use a real browser - get firefox!! Recent changes RSS feed Valid XHTML 1.0