Both sides previous revision
Poprzednia wersja
Nowa wersja
|
Poprzednia wersja
|
pl:dydaktyka:jimp2:2017:labs:dziedziczenie [2018/04/18 14:45] mwp [Funkcje wirtualne] |
pl:dydaktyka:jimp2:2017:labs:dziedziczenie [2019/06/27 15:50] (aktualna) |
// 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> |
* AndQuery <code>AndQuery(std::unique_ptr<Query> left, std::unique_ptr<Query> right)</code> | * 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: |
</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> |