Różnice

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

Odnośnik do tego porównania

Both sides previous revision Poprzednia wersja
Nowa wersja
Poprzednia wersja
pl:dydaktyka:jimp2:2017:labs:dziedziczenie [2017/07/17 10:08]
127.0.0.1 edycja zewnętrzna
pl:dydaktyka:jimp2:2017:labs:dziedziczenie [2019/06/27 15:50] (aktualna)
Linia 125: Linia 125:
 {{.:​umowa.png|Umowa - diagram UML}} {{.:​umowa.png|Umowa - diagram UML}}
  
-Taka relacja umożliwia stworzenie tylko jednej klasy //​Pracownik//,​ która będzie miała jedno pole //Umowa//, któremu z kolei będzie przypisywana ​abo //​UmowaDzielo//​ albo //​UmowaPraca//​. W takim wypadku po rzutowaniu //​UmowaDzielo//​ lub //​UmowaPraca//​ na typ bazowy //Umowa//, musi być możliwe wywołanie metod odpowiednio obliczających wynagrodzenie netto. W tym celu stosowane są **funkcje wirtualne**.+Taka relacja umożliwia stworzenie tylko jednej klasy //​Pracownik//,​ która będzie miała jedno pole //Umowa//, któremu z kolei będzie przypisywana ​albo //​UmowaDzielo//​ albo //​UmowaPraca//​. W takim wypadku po rzutowaniu //​UmowaDzielo//​ lub //​UmowaPraca//​ na typ bazowy //Umowa//, musi być możliwe wywołanie metod odpowiednio obliczających wynagrodzenie netto. W tym celu stosowane są **funkcje wirtualne**.
  
 **Funkcja wirtualna**,​ to taka metoda klasy, która nadpisana w klasie pochodnej, nawet po rzutowaniu obiektu na typ bazowy zachowa swoją implementacje. Klasa bazowa z funkcją wirtualną będzie //​polimorficzna//​ - w zależności od tego jaki obiekt klasy pochodnej był na nią rzutowany, taka implementacja metody zostanie uruchomiona. **Funkcja wirtualna**,​ to taka metoda klasy, która nadpisana w klasie pochodnej, nawet po rzutowaniu obiektu na typ bazowy zachowa swoją implementacje. Klasa bazowa z funkcją wirtualną będzie //​polimorficzna//​ - w zależności od tego jaki obiekt klasy pochodnej był na nią rzutowany, taka implementacja metody zostanie uruchomiona.
Linia 132: Linia 132:
 #include <​iostream>​ #include <​iostream>​
 #include <​list>​ #include <​list>​
 +#include <​memory>​
  
 using namespace std; using namespace std;
  
 class Umowa{ class Umowa{
 +  public:
 +    Umowa(double pensja):​wynagrodzenie_brutto_(pensja){};​
 +    virtual double PobierzNetto() const;
 +    double PobierzBrutto() const;
   protected:   protected:
-    double ​wynagrodzenieBrutto;​ +    double ​wynagrodzenie_brutto_;
-  public: +
-    Umowa(double pensja):​wynagrodzenieBrutto(pensja){};​ +
-    virtual double pobierzNetto();​ +
-    double pobierzBrutto();+
 }; };
  
 class UmowaDzielo:​ public Umowa{ class UmowaDzielo:​ public Umowa{
   public:   public:
-    UmowaDzielo(double pensja):​Umowa(pensja){}; +    UmowaDzielo(double pensja):​Umowa{pensja{}; 
-    virtual double ​pobierzNetto();+    virtual double ​PobierzNetto() const override;
 }; };
  
-class UmowaPraca: public Umowa{+class UmowaPraca: public Umowa {
   public:   public:
-    UmowaPraca(double pensja):​Umowa(pensja){}; +    UmowaPraca(double pensja):​Umowa{pensja{}; 
-    virtual double ​pobierzNetto();+    virtual double ​PobierzNetto() const override;
 }; };
  
 class Pracownik{ class Pracownik{
 +  public:
 +    Pracownik(string imie,string nazwisko,​string pesel,​std::​unique_ptr<​Umowa>​ umowa)
 +      :​imie_{imie},​nazwisko_{nazwisko},​pesel_{pesel},​umowa_{move(umowa)}{};​
 +    double PobierzPensje() const;
 +    friend std::​ostream&​ operator<<​(std::​ostream&,​Pracownik&​);​
   private:   private:
-    string ​imie,nazwisko,pesel+    ​std::string ​imie_,nazwisko_,pesel_
-    ​Umowa* umowa; +    ​std::unique_ptr<Umowa> umowa_;
-  public: +
-    Pracownik(string i,string n,string p,Umowa* u) +
-      ​:imie(i),​nazwisko(n),​pesel(p),​umowa(u){};​ +
-    Pracownik(const Pracownik&​);​ +
-    ~Pracownik(){};​ +
-    double pobierzPensje();​ +
-    friend ostream&​ operator<<​(ostream&,​Pracownik&​);+
 };</​code>​ };</​code>​
  
Linia 365: Linia 364:
     // MUSI jednak być zaimplementowana w pochodnej (lub ponownie ​     // MUSI jednak być zaimplementowana w pochodnej (lub ponownie ​
     // zadeklarowana jako czysta     // zadeklarowana jako czysta
-    virtual double ​pobierzNetto() = 0;+    virtual double ​PobierzNetto() = 0;
 }; };
 </​code>​ </​code>​
Linia 456: Linia 455:
  
   - [2 plusy] Zdefiniować metody wypełniania tablicy std::​vector<​int>:​   - [2 plusy] Zdefiniować metody wypełniania tablicy std::​vector<​int>:​
-    * jednorone (zawsze ta sama wartość), z wartością domyślną 0 +    * jednorone (zawsze ta sama wartość), z wartością domyślną 0 konstruktor klasy: <code cpp>​UniformFill(int value = 0)</​code>​ 
-    * z inkrementacją (uwzględniająca wartość początkową start i krok step, który ma wartość domyślną 1) +    * z inkrementacją (uwzględniająca wartość początkową start i krok step, który ma wartość domyślną 1) <code cpp>​IncrementalFill(int start, int step = 1)</​code>​ 
-    * za pomocą generatora liczb losowych +    * za pomocą generatora liczb losowych ​<code cpp>​RandomFill(std::​unique_ptr<​std::​default_random_engine>​ generator, std::​unique_ptr<​std::​uniform_int_distribution<​int>>​ distribution)</​code>​ 
-    * z kwadratem indeksu (a*index^2+b),​ zarówno a i b mogą przyjąć domyślne wartości+    * z kwadratem indeksu (a*index^2+b),​ zarówno a i b mogą przyjąć domyślne wartości ​<​code>​SquaredFill(int a = 1, int b = 0)</​code>​
   - [3 plusy] Przygotować klasę abstrakcyjną StudentComparator z abstrakcyjną metodą <code cpp>bool IsLess(const Student &left, const Student &​right)</​code>​ i zdefiniowanym operatorem wywołania funkcji, delegującym zachowanie do abstrakcyjnej meteody. Następnie zdefiniować różne implementacje porównywania studentów:   - [3 plusy] Przygotować klasę abstrakcyjną StudentComparator z abstrakcyjną metodą <code cpp>bool IsLess(const Student &left, const Student &​right)</​code>​ i zdefiniowanym operatorem wywołania funkcji, delegującym zachowanie do abstrakcyjnej meteody. Następnie zdefiniować różne implementacje porównywania studentów:
     * ByFirstNameAscending     * ByFirstNameAscending
Linia 475: Linia 474:
     * [2 plusy]     * [2 plusy]
       * OrQuery ​       * OrQuery ​
-      * AndQuery+      * AndQuery ​<​code>​AndQuery(std::​unique_ptr<​Query>​ left, std::​unique_ptr<​Query>​ right)</​code>​
   - [1 plus] Przetestuj przykład z sekcji [[#​metody_skladowe|Metody składowe]]   - [1 plus] Przetestuj przykład z sekcji [[#​metody_skladowe|Metody składowe]]
-  - **[1 punkt] Wykorzystując klasę [[.:​klasy1#​deklaracja_klasy|Punkt]],​ napisz klasę Punkt3D dziedziczącą po niej i implementującą dodatkowo metodę //double distance(Punkt3D)//​. W każdym z konstruktorów i destruktorów klas Punkt i Punkt3D wypisz na ekran jakąś wiadomość i zaobserwuj w jakiej kolejności wywołują się konstruktory i destruktory.** 
-  - **[1 punkt] Mając dwa obiekty, jeden klasy Punkt a drugi klasy Punkt3D o nazwach //punkt2d// i //​punkt3d//,​ wywołaj punkt2d.distance(punkt3d). Co sie stało?** 
-  - **[1 punkt] W klasie Punkt2D istnieje przeciążony operator wpisywania do strumienia ("<<"​). Co się stanie jeśli będziesz chciał wypisać obiekt klasy Punkt3D w następujący sposób:** <code cpp>​Punkt3D p3d(1,2,3); 
-cout << p3d << endl;</​code>​ 
   - [3 plusy] Napisz dwie klasy: Kolo i Kula. Klasa Kolo powinna być klasą bazową dla klasy Kula.   - [3 plusy] Napisz dwie klasy: Kolo i Kula. Klasa Kolo powinna być klasą bazową dla klasy Kula.
     * W klasie Kolo powinny znaleźć się następujące pola i metody:     * W klasie Kolo powinny znaleźć się następujące pola i metody:
Linia 499: Linia 494:
 </​code>​ </​code>​
   - [2 plusy] Dopisz brakujące metody z przykładu [[#​funkcje_wirtualne|Funkcje wirtualne]] i uruchom program.   - [2 plusy] Dopisz brakujące metody z przykładu [[#​funkcje_wirtualne|Funkcje wirtualne]] i uruchom program.
-  - **[2 punkty] Napisz klasę abstrakcyjną //​Ksztalt//,​ która będzie posiadała jedna czystą metodę wirtualną //rysuj//. Następnie napisz kilka klasę dziedziczących po tej klasie (//​Trójkąt,​ Kwadrat, Koło//) i odpowiednio implementujących metodę //rysuj//. Metoda powinna rysować kształty w trybie tekstowym. \\ Zadeklaruj listę, która przechowuje wskaźniki na obiekty klasy //Ksztalt// i uzupełnij ją losowo //Kolami, Kwadratami//​ albo //​Trojkatami//​. Następnie wywołaj na każdym obiekcie z listy metodę //rysuj//. Jaki jest tego efekt?** 
  
 +====== Zadanie domowe: ======
 +
 +Iteratory to uogólnienie wskaźników wykorzystywanych do iteratowania po tablicy. W c żeby przeiterować po tablicy i tylko odczytać jej elementy można było wykonać następujący kod:
 +<code c>
 +  int tab[128];
 +  int *p;
 +  int *tab_end = &​tab[128];​
 +  int value;
 +  InitTab(tab);​
 +  for (p = &​tab[0];​ p != tab_end; ++p) {
 +     value = *p;
 +  }
 +</​code>​
 +Wraz z c++ przyszły klasy i przeciążanie operatorów i w tym języku da się uogólnić ten schemat na dowolny obiekt, który posiada odpowiednioo zdefiniowane metody:
 +<code cpp>
 +  array<​int,​128>​ tab;
 +  array<​int,​128>::​iterator p;
 +  array<​int,​128>::​iterator tab_end = tab.end();
 +  int value;
 +  InitTab(tab);​
 +  for (p = tab.begin();​ p != tab_end; ++p) {
 +     value = *p;
 +  }
 +</​code>​
 +Ze względu na to, że kompilator udziela niezbyt jasnych informacji o błędach i o tym co jest niezbędne do zaimplementowania,​ spróbujemy wykorzystać dziedziczenie. W tym celu zdefiniujemy klasę bazową która mogłaby być wykorzystywana przez dowolną implementację iteratora:
 +  - **[1 punkt]** (lab7_iterable_tests) przygotować klasę bazową //​IterableIterator//​ udostępniającą następujący zestaw metod bez zdefiniowanego zachowania (abstrakcyjnych,​ czysto wirtualnych),​ wszystkie te metody mają być przesłanialne przez klasy pochodne, zastanowić się które z tych metod nie powinny modyfikować **this**:
 +    * <code cpp>​std::​pair<​int,​ std::​string>​ Dereference()</​code>​ - odpowiada operacji //cos = *it//, czyli jej implementacja w klasach pochodnych powinna zwracać wartość pokazywaną przez iterator w akutalnym stanie. Nie umożliwiamy edycji tego stanu, stąd wartość zwracana jest przez kopię, a nie referencję.
 +    * <code cpp>​IterableIterator &​Next()</​code>​ - odpowiada operacji //++it// jej implementacja w klasie pochodnej powinna przesuwać wskaźnik na następną wartość w sekwencji, jeśli nie ma następnej wartości powinna przesuwać wskaźnik tuż za koniec
 +    * <code cpp>bool NotEquals(const IterableIterator &​other)</​code>​ - odpowiada operacji //it != other//, jej implementacja w klasie pochodnej powinna zwracać prawdę jeśli iterator other wskazuje na ten sam element sekwencji (względnem jego indeksu w sekwencji, a nie tylko jego wartości zwracanej przez //​Dereference()//​).
 +    * Destruktor - jako jedyna metoda w tej klasie powinien mieć domyślną implementację,​ najlepiej skorzystać z pomocy kompilatora i kazać mu ją wygenerować (= defualt). ​
 +  - **rozgrzewka** Należy zaimplementować klasę pochodną dla **IterableIterator**,​ a mianowicie: **ZipperIterator**,​ zipper iterator powinien być w stanie przeiterować po dwóch wektorach na raz, biorąc po kolei pierwszy element z pierwszego wektora i pierwszy element z drugiego wektora, drugi element z pierwszego i drugi element z drugiego, itd.. Najłatwiej to zrobić jeśli ZipperIterator będzie posiadał po dwa const iteratory do początu i końca obu wektorów. Wtedy Derefencja, Next i NotEquals są bardzo proste w implementacji. (Testy wymagają konstuktora postaci: <code cpp>​explicit ZipperIterator(std::​vector<​int>::​const_iterator left_begin,
 +                 ​std::​vector<​std::​string>::​const_iterator right_begin,​
 +                 ​std::​vector<​int>::​const_iterator left_end,
 +                 ​std::​vector<​std::​string>::​const_iterator right_end);</​code>​
 +  - **[1 punkt]** przygotować ​
 +    - klasę IterableIteratorWrapper opakowującą dowloną podklasę IterableIterator i udostępniającą operatory i fukncje potrzebne by klasa mogła być używana jako iterator c++ między innymi w rage for, czyli (zastanowić się, które z tych funkcji muszą być typu const):
 +      * konstruktor:​ <code cpp>​IterableIteratorWrapper(std::​unique_ptr<​IterableIterator>​ iterator)</​code> ​
 +      * <code cpp>bool operator!=(const IterableIteratorWrapper &​other)</​code>​ - powinna wywoływać NotEquals z przekazanych iteratorach (pole składowe i argument fukncji)
 +      * <code cpp>​std::​pair<​int,​ std::​string>​ operator*()</​code>​ - powinna wywoływać Dereference na polu składowym
 +      * <code cpp>​IterableIteratorWrapper &​operator++()</​code>​ - powinna wywoływać Next na polu składowym
 +    - przygotować klasę bazową Iterable o następujących metodach (zastanowić się, które z tych funkcji muszą być typu const):
 +      * <code cpp>​std::​unique_ptr<​IterableIterator>​ ConstBegin()</​code>​ - czysto abstrakcyjna metoda (bez domyślnej implementacji) przesłanialna w klasach pochodnych ma za zadanie zwracanie odpowiedniego iteratora do początku sekwencji dla właściwej klasy.
 +      * <code cpp>​std::​unique_ptr<​IterableIterator>​ ConstEnd()</​code>​ - czysto abstrakcyjna metoda (bez domyślnej implementacji) przesłanialna w klasach pochodnych ma za zadanie zwracanie odpowiedniego iteratora za końcem sekwencji dla właściwej klasy.
 +      * <code cpp>​IterableIteratorWrapper cbegin() const</​code>​ - konkretna metoda (nie wirtualna), która wywołuje ConstBegin i tworzy odpowiedni typ do zwrócenia.
 +      * <code cpp>​IterableIteratorWrapper cend() const</​code>​ - konkretna metoda (nie wirtualna), która wywołuje ConstEnd i tworzy odpowiedni typ do zwrócenia.
 +      * <code cpp>​IterableIteratorWrapper begin() const</​code>​ - konkretna metoda (nie wirtualna), która wywołuje cbegin()
 +      * <code cpp>​IterableIteratorWrapper end() const</​code>​ - konkretna metoda (nie wirtualna), która wywołuje cend()
 +  - **[3 punkty]** (lab7_iterable_zipper_tests,​ lab7_iterable_product_tests,​ lab7_iterable_enumerate_tests)przygotować klasy Zipper, Product, Enumerate, które implementują klasę Iterable i klasy ProductIterator i EnumerateIterator,​ które implementują klasę IterableIterator.
 +    * klasa zipper ma za zadanie utworzenie możliwości przeglądania dwóch wektorów jednocześnie w pętli typu range for.
 +    * klasa enumerate ma za zadanie utworzenie możliwości przeglądania jednego wektora w pętli typu range for, ale tak, że każda wartość wskazywana przez jej iterator zwraca parę indeks tego elementu, wartość tego elementu.
 +    * klasa product ma za zadanie utworzenie możliwości przeglądania dwóch wektorów jednocześnie w pętli typu range for, ale w przeciwieństwie do zipper, ma tworzyć iloczy kartezjański wszystkich par <code cpp>
 +  const vector<​int>​ vi {4, 77, -91};
 +  const vector<​string>​ vs {"​4",​ "​9991",​ "​adfskld"​};​
 +
 +  for (const auto &p : Zipper(vi, vs)) {
 +    cout << "​("​ << p.first << ", \""​ << p.second << "​\"​) ";
 +  }
 +  //wypisze: (4, "​4"​) (77, "​9991"​) (-91,"​adfskld"​)
 +  ​
 +  for (const auto &p : Enumerate(vs)) {
 +    cout << "​("​ << p.first << ", \""​ << p.second << "​\"​) ";
 +  }
 +  //wypisze: (0, "​4"​) (1, "​9991"​) (2,"​adfskld"​)
 +  ​
 +  for (const auto &p : Product(vi,​vs)) {
 +    cout << "​("​ << p.first << ", \""​ << p.second << "​\"​) ";
 +  }
 +  //wypisze: (4, "​4"​) (4,"​9991"​) (4, "​adfskld"​) (77, "​4"​) (77,"​9991"​) (77, "​adfskld"​) (-91,"​4"​) (-91,"​9991"​) (-91, "​adfskld"​) ​
 +
 +</​code>​
pl/dydaktyka/jimp2/2017/labs/dziedziczenie.1500278928.txt.gz · ostatnio zmienione: 2019/06/27 15:52 (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