Różnice

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

Odnośnik do tego porównania

pl:dydaktyka:jimp2:2017:labs:klasy2 [2017/03/28 01:50]
mwp [Klasy i obiekty II]
pl:dydaktyka:jimp2:2017:labs:klasy2 [2019/06/27 15:50]
Linia 1: Linia 1:
-====== Klasy i obiekty II ====== 
- 
-===== Zasady żądzące życiem obiektu ===== 
- 
-Zapoznać się z regułami przedstawionymi w dokumentacji na stacku 
-[[http://​stackoverflow.com/​documentation/​c%2b%2b/​1206/​the-rule-of-three-five-and-zero#​t=201703272317519853522|Zasada zera, zasada pięciu, zasada trzech]]. 
- 
- 
-<file cpp XXX.h> 
-class XXX { 
-  //w zeszłym odcinku: 
-  //domyślny konstruktor 
-  XXX(); ​ 
-  //​konstruktory parametryczne 
-  XXX(int param); 
-  XXX(const std::string name); 
-  ​ 
-  //Rule of five:// 
-  //1. konstruktor kopiujący 
-  XXX(const XXX &xxx); 
-  //2. konstruktor przenoszący 
-  XXX(XXX &&​xxx);​ 
-  //3. operator przypisania kopiujący 
-  XXX &​operator=(const XXX &xxx); 
-  //4. operator przypisania przenoszący 
-  XXX &​operator=(XXX &&​xxx);​ 
-  //5. Destruktor 
-  ~XXX(); 
-} 
-</​file>​ 
- 
-**Reguła 3** była stosowana dla C%%++%%03 przed wprowadzeniem referencji do r-wartości i obejmowała funkcje: 1, 3, 5. **Reguła 5** mówi, że jeśli ktoś potrzebuje przedefiniować jedną z tych metod to najprawdopodobniej musi to zrobić z wszystkimi 5. 
- 
-==== Za co właściwie odpowiadają te metody? ==== 
- 
-Metody te są wywoływane w trakcie życia obiektu w celu oddelegowania odpowiedzialności za zarządzanie zasobami do samego obiektu. Są to operacje konstruowania obiektu jeszcze nie istniejącego na podstawie innego (już zainicjalizowanego),​ przypisywanie innego obiektu do istniejącego gdy oba już są zainicjalizowane lub niszczenie obiektu. 
- 
-Przykład: 
-<file cpp main.cpp>​ 
-int main() { 
-  //​Konstrukcja obiektu za pomocą domyślnego konstruktora 
-  //​nieciekawe 
-  XXX old_xxx {}; 
-  ​ 
-  //​Konstrukcja obiektu na podstawie już zaincjalizowanego 
-  XXX new_xxx {old_xxx}; ​ 
-  ​ 
-  //znowu nieciekawy konstruktor domyślny... 
-  XXX another_xxx {}; 
-  ​ 
-  //tutaj przypisujemy stan obiektu jednego do drugiego 
-  //ale obydwa są już zaincjalizowane... 
-  XXX another_xxx = new_xxx; 
-  ​ 
-  //tutaj kończy się zakres funkcji main i wszystkie trzy obiekty tracą ważność 
-  //zostają wywołane więc destruktory 
-} 
-</​file>​ 
- 
-==== A co z przenoszącymi wersjami z C++11 ==== 
- 
-W przypadku kopiowania stary obiekt zostaje w nienaruszonym stanie (był zawsze przekazywany jako const &). Jeśli dokonujemy przenoszenia stary obiekt jest niszczony, a jego stan jest przenoszony do nowej lokalizacji,​ czyli liczba obiektów pozostaje prawie stała... 
- 
-Przykład: 
-<file cpp main.cpp>​ 
-int movement_example() { 
-  //​Konstrukcja obiektu za pomocą domyślnego konstruktora 
-  //​nieciekawe 
-  XXX old_xxx {}; 
-  ​ 
-  //​Konstrukcja obiektu na podstawie już zaincjalizowanego 
-  //z przeniesieniem old_xxx jest niszczony 
-  XXX new_xxx {move(old_xxx)}; ​ 
-  ​ 
-  //znowu nieciekawy konstruktor domyślny... 
-  XXX another_xxx {}; 
-  ​ 
-  //tutaj przypisujemy stan obiektu jednego do drugiego 
-  //ale obydwa są już zaincjalizowane... 
-  //stan new_xxx teraz będzie w another_xxx,​ a sam new_xxx jest niszczony 
-  XXX another_xxx = move(new_xxx);​ 
-  ​ 
-  //tutaj kończy się zakres funkcji main i wszystkie trzy obiekty tracą ważność 
-  //zostają wywołane więc destruktory (na wszystkich trzech, ale tylko jeden ma ważny stan) 
-} 
-</​file>​ 
-===== Konstruktory kopiujące ===== 
-Oprócz trzech rodzajów konstruktorów wymienionych na poprzednich laboratoriach:​ konstruktora domyślnego,​ konstruktora parometrowego oraz konstruktora bezparametrowego istnieje jeszcze jeden specjalny typ konstruktora,​ tzw. **konstruktor kopiujący**. ​ 
- 
-Konstruktor kopiujący jest przydatny w chwili, kiedy klasy posiadają pola dynamicznie alokowane. Konstruktor kopiujący **musi** przyjmować jako parametr referencje do obiektu. Przykład konstruktora kopiującego dla klasy Punkt z poprzednich laboratoriów (nie jest on w tej klasie wymagany). Deklaracja:<​code cpp>... 
-Punkt(const Punkt&​);​ 
-...</​code>​ 
-Definicja:<​code cpp>... 
-Punkt::​Punkt(const Punkt &​punkt){ 
-this->x = punkt.x; 
-this->y = punkt.y; 
-cout << "​Konstruktor kopiujący!"​ << endl; 
-} 
-... 
-</​code>​ 
- 
-Konstruktor kopiujący jest wywoływany automatycznie w następujących sytuacjach: 
-  * Podczas przekazywania obiektów przez wartość do funkcji (tworzone są ich lokalne kopie), 
-  * Podczas zwracania obiektów przez funkcje, ​ 
-  * Podczas deklaracji obiektu gdy jako parametr konstruktora podany zostanie inny obiekt. 
-  * **Ważne!** Podczas **deklaracji** obiektu połączonej z inicjalizacją przy użyciu operatora **=** (przypisania). W innych przypadkach przypisania poza deklaracją,​ konstruktor kopiujący nie zadziała!<​code cpp>... 
-Punkt p(12,34); 
-Punkt p2(p); ​  // Konstruktor kopiujący. 
-Punkt p3 = p;  // Konstruktor kopiujący. 
-Punkt p4; 
- 
-//NIE ZADZIAŁA KONSTRUKTOR KOPIUJĄCY 
-//TYLKO DOMYŚLNY OPERATOR PRZYPISANIA 
-p4 = p2; 
-</​code>​ 
- 
-===== Klasy i const===== 
-Słowo kluczowe **const** ma różne znaczenie w zależności od kontekstu w jakim jest stosowane. 
-====Metody const==== 
-W odniesieniu do metod klasy oznacza, że dana metoda nie może modyfikować elementów składowych klasy jak również nie może wywoływać innych metod niż te zdeklarowane jako **const**. Jeśli można, warto zadeklarować metodę jako //const//. Umożliwi to poprawne korzystanie z obiektów tej klasy zadeklarowanych jako stałe.<​code cpp> 
-class Matrix{ 
-  ... 
-  void wyswietl() const; 
-  ... 
-}; 
-</​code>​ 
-====Zmienne/​obiekty const==== 
-W odniesieniu do zmiennych i obiektów i innych zmiennych, oznaczają że nie można modyfikować zmiennej/​obiektu. Innymi słowy nie można wywoływać na rzecz danego obiektu innych metod niż zadeklarowane jako **const**<​code cpp>... 
-const Matrix m(["1 2 3; 1 2 3"]); 
- 
-//Metoda ustawiająca wartość w komórce macierzy o indeksie (1,1) 
-//​Wywołanie takiej metody wygeneruje błąd podczas kompilacji. 
-m.set(1,​1,​12);  ​ 
- 
-//Gdyby metoda wyświetl nie była zadeklarowana jako const 
-//takie wywołanie też spowodowałoby błąd 
-m.wyswietl();​ 
-</​code>​ 
- 
- 
-=====Funkcje i klasy friend===== 
-Funkcja i klasy zaprzyjaźnione mają nieograniczony dostęp do wszystkich pól i metod klasy której są //​przyjaciółmi//​. 
-====Funkcje zaprzyjaźnione==== 
-Funkcja zaprzyjaźniona definiowana jest poza zasięgiem klasy, ale informacja o tym że jest ona //​przyjacielem//​ musi znaleźć się w klasie: 
-<code cpp> 
-//Plkik Matrix.h 
-class Matrix{ 
-  double** data; 
-  ... 
-  friend void wyzeruj(Matrix&​ m); 
-  ... 
-}; 
- 
-// Plik main.cpp 
- 
-// Funkcja może modyfikować prywatne dane klasy 
-// Matrix, ponieważ została określona jako friend 
-void wyzeruj(Matrix&​ matrix){ 
-  for(int r  = 0; r < matrix.rows;​ r++) 
-    for(int c  = 0; c < matrix.cols;​ c++) 
-      matrix.data[r][c] = 0; 
-} 
-</​code>​ 
-====Klasy zaprzyjaźnione==== 
-Analogicznie do funkcji klasy zaprzyjaźnione maja nieograniczony dostęp d wszystkich pól i metod klasy które są przyjaciłmi. Jeśli klasa Node ma być przyjacielem klasy Lista, to w tej drugiej należy umieścić następującą deklarację:​ 
-<code cpp> 
-class Lista{ 
-  ... 
-  friend class Node; 
-  ... 
-}; 
-</​code>​ 
- 
- 
-===== Słowo kluczowe static===== 
-Słowo kluczowe //static// może być stosowane zarówno w stosunku do pól klasy jak i jej metod, ale w obu przypadkach ma nieco inne znaczenie. 
- 
-==== Pola statyczne ==== 
-Pola statyczne są swego rodzaju zmiennymi globalnymi, należącymi jednak do zasięgu klasy. Zmienna statyczna jest współdzielona przez wszystkie obiekty klasy. Do zmiennych statycznych można odwoływać się nie mając utworzonego obiektu. 
- 
-Słowo kluczowe **static** dodaje się tylko i wyłącznie podczas deklaracji zmiennej. Użycie słowa kluczowego //static// podczas inicjalizacji zmiennej powoduje błąd składniowy. 
- 
-Deklaracja zmiennej statycznej w pliku nagłówkowym:​ 
-<code cpp> 
-class Matrix{ 
-  public: 
-    static int licznik; 
-    Matrix(){licznik++;​} 
-   ​~Matrix(){licznik--;​} 
-}; 
-</​code>​ 
- 
-Zmienna statyczna musi zostać zainicjalizowana w przestrzeni pliku. Nie można inicjalizować statycznej zmiennej w konstruktorze,​ lub innej funkcji. Inicjalizacja zmiennej statycznej w pliku cpp: 
-<code cpp> 
-#include "​Matrix.h"​ 
- 
-int Matrix::​licznik = 0; 
-... 
-</​code>​ 
- 
-Odwoływanie się do pól statycznych:​ 
-<code cpp> 
-int main(){ 
-  Matrix m; 
-  cout << Matrix::​licznik <<​endl;​ 
-  cout << Matrix.licznik << endl; 
-} 
-</​code>​ 
- 
-====Metody statyczne==== 
-Metody statyczne można wywoływać bez konieczności tworzenia obiektów klasy. Można z tego wywnioskować,​ że metody statyczne **nie posiadają** wskaźnika **this**. 
- 
-Nie można zatem wewnątrz metod statycznych wywoływać żadnych innych metod niestatycznych,​ ani odwoływać się do pól niestatycznych. 
- 
-Metody statyczne należą jednak do zasięgu klasy i mają dostęp do wszystkich pól klasy: 
-<code cpp> 
-class Matrix{ 
-  ... 
-  static void wyswietl(Matrix&​ m); 
-}; 
- 
-void Matrix::​wyswietl(Matrix & m){ 
-  for(int r  = 0; r < matrix.rows;​ r++){ 
-    for(int c  = 0; c < matrix.cols;​ c++) 
-      cout << matrix.data[r][c] << endl; 
-    cout << endl; 
-  } 
-} 
-</​code>​ 
- 
- 
- 
-====== Ćwiczenia ====== 
-  - **[1 punkt] Dopisz konstruktor kopiujący do klasy Punkt z poprzedniego laboratorium i prześledź jak wyglądają wywołania konstruktorów. Zobacz, że przypisanie obiektów poza deklaracją nie wywołuje konstruktora kopiującego.** 
-  - **[1 punkt] Sprawdź jak zachowa się DTab bez konstruktora kopiującego. Na przykład napisz funkcję, która będzie wypełniać DTab podaną jako parametr liczbą i zwracać ją. Funkcja powinna mieć następujący nagłówek** <code cpp>DTab wypelniona(int wypelnienie);</​code>​ **Co się stanie gdy w zwróconej DTab będziemy chcieli zmienić jakieś elementy, albo ją rozszerzyć?​ Dopisz do klasy konstruktor kopiujący.** 
-  - {{ .:​lista.png?​300|Lista powiązana}} [3 plusy] Zaimplementuj listę powiązaną przechowującą obiekty klasy //string//. Tablice nie są zawsze wystarczająco dobrym mechanizmem do przechowywania danych, zwłaszcza jeśli nie wiemy dokładnie ile tych danych będzie. Dynamiczne tablice (jak ta implementowana na poprzednich zajęciach) są pewnym rozwiązaniem,​ ale niezbyt wydajnym pod względem pamięci - realokacja pamięci, alokacja zbędnej pamięci, etc. \\ Lista powiązana składa się z dwóch elementów (patrz rysunek obok): 
-    * Nadrzędnego obiektu udostępniającego interfejs służący do dodawania, usuwania, wyszukiwania elementów w liście. Posiada on wskaźnik do głowy listy (patrz poniżej). Może przechowywać informacje na temat długości listy. 
-    * Elementów składowych listy, tzw. Węzłów, z których każdy posiada pole przechowujące dane i pole będące wskaźnikiem do następnego węzła. Obiekt nadrzędny posiada jedynie wskaźnik do pierwszego elementu listy (tak zwanej głowy). Odnajdywanie //n-tego// elementu listy odbywa się poprzez "​przeskakiwanie"​ po węzłach aż do szukanego elementu. \\ __Pamiętaj o destruktorze i konstruktorze kopiującym__ :!: 
-  - [1 plus] Napisz dwie klasy: Rodzic i Dziecko. Klasa Rodzic powinna mieć takie pola jak imię, nazwisko, wiek, oraz dziecko (zakładamy dla uproszczenia,​ że rodzic ma tylko jedno dziecko :) ). Dziecko powinno mieć takie pola jak imię, nazwisko, wiek, szkoła. Zdefiniuj klasę Rodzic jako zaprzyjaźnioną klasy Dziecko. Przetestuj, czy można modyfikować zmienne prywatne klasy Dziecko z poziomu metod klasy Rodzic. Napisz na przykład metodę //​przepiszDoInnejSzkoly(string nazwa)// która będzie zmieniać szkołę dziecka operując bezpośrednio na jego danych. \\ Następnie usuń linijkę odpowiedzialną za określenie klasy zaprzyjaźnionej i spróbuj skompilować ponownie program. 
-  - [3 plusy] Napisz klasę Marsjanin, która będzie miała statyczne pole //​liczbaMarsjan//,​ określające liczbę stworzonych obiektów Marsjanin. Każdy Marsjanin powinien atakować gdy liczba wszystkich Marsjan jest większa od 5 i ukrywać się w przeciwnym wypadku. \\ Napisz program który w pętli nieskończonej będzie tworzył lub usuwał obiekty klasy Marsjanin i wywoływał metodę //atakuj// dla wszystkich Marsjan. Obiekty powinny być przechowywane w liście (zobacz [[http://​www.cplusplus.com/​reference/​stl/​list/​|List]]). 
-  - **[3 punkty] Zaimplementuj klasę o nazwie Matrix, która będzie reprezentować macierz o dowolnych rozmiarach. Wymagania dotyczące klasy Matrix:** 
-  * Klasa powinna wewnętrznie reprezentować macierz przy pomocy tablicy dwuwymiarowej obiektów typu Complex (wykorzystaj klasę napisaną na poprzednim laboratorium). Umawiamy się, że liczba zespolona zapisywana jest w następujący sposób: <​code>​4.5i6</​code>​ Co oznacza w zapisie matematycznym <​code>​4.5 + 6i</​code>​ 
-  * Klasa Matrix powinna posiadać konstruktor parometrowy określający jej wymiary, konstruktor bezparametrowy,​ oraz kopiujący. Dopisać konstruktor,​ który będzie przyjmować napis //const char*// (lub obiekt klasy string z biblioteki //​string.h//​) w notacji Matlaba i parsować go, aby można było stworzyć obiekt Matrix w taki sposób: <code cpp> Matrix m("​[1i3 2i5 3; 3 4 5; 6 7 8]"​);</​code> ​ 
-  * Klasa Matrix powinna udostępniać metody pozwalające na ustawianie/​pobieranie jej elementów 
-  * Klasa Matrix powinna udostępniać metody umożliwiające następujące operacje: 
-    * Dodawanie macierzy. Metoda powinna klasy powinna pobierać jako parametr inny obiekt klasy Matrix i zwracać wynik będący sumą macierzy reprezentowanej przez obiekt na rzecz którego wywoływana jest metoda i macierzy reprezentowanej przez obiekt podany jako parametr. Przykładowe wywołanie:<​code cpp>​Matrix m("[1 2 3;3 4 5; 2 3 4]"); 
-Matrix m2("[3 2 1; 5 4 3; 7 6 5]"); 
-Matrix wynik = m.add(m2); 
-</​code>​ 
-    * Odejmowanie macierzy (pozostałe analogicznie) 
-    * Mnożenie macierzy zarówno przez inna macierz jak i przez liczbę (w tym wypadku Complex) 
-    * Dzielenie macierzy zarówno przez inna macierz jak i przez liczbę (w tym wypadku Complex) 
-    * Podnoszenie macierzy do potęgi! 
-    * Metodę //print// wyświetlającą dana macierz w formacie Matlaba, z ładnym formatowaniem (podział na wiersze) 
-  * Zabezpiecz program przed sytuacjami wyjątkowymi! Na przykład: dodawanie macierzy o różnych wymiarach, mnożenie macierzy o nieodpowiednich wymiarach, odwracanie macierzy zerowej. 
- 
-  * Napisz program który umożliwi testowanie klasy Matrix. Program powinien przyjmować jako parametr dwie macierze zapisane w formacie Matlaba. Funkcja //main// powinna wyglądać następująco:<​code cpp>#​include <​iostream>​ 
-#include "​Matrix.h"​ 
- 
-int main(int argc, char* argv[]){ 
-  Matrix m1(argv[1]);​ 
-  Matrix m2(argv[2]);​ 
- 
-  cout << "​Macierz pierwsza: " << m1.print() << endl; 
-  cour << "​Macierz druga: " << m2.print() << endl; 
- 
-  cout << "​Dodawanie"​ << (m1.add(m2)).print() << endl; 
-  cout << "​Odejmowanie"​ << (m1.sub(m2)).print() << endl; 
-  cout << "​Mnożenie"​ << (m1.mul(m2)).print() << endl; 
-  cout << "​Dzielenie"​ << (m1.div(m2)).print() << endl; 
-  cout << "​Potęgowanie"​ << (m1.pow(2)).print() << endl; 
-  cout << "​Potęgowanie"​ << (m2.pow(2)).print() << endl; 
-} 
-</​code>​ 
- 
-<WRAP center round info 60%> 
-Zabezpieczenie programu przed sytuacjami wyjątkowymi w "​podstawowej"​ wersji, wymaganej na laboratorium,​ związane jest ze sprawdzeniem odpowiedniej wartości i wypisaniem komunikatu dla użytkownika. Bardziej profesjonalny sposób obsługi takich sytuacji związany jest z **mechanizmem wyjątków**. Jeżeli chcesz dowiedzieć się o nim czegoś więcej, zapraszam do zapoznania się z nieobowiązkową instrukcją do laboratorium [[.:​wyjatki|Wyjątki]]. 
-</​WRAP>​ 
- 
  
pl/dydaktyka/jimp2/2017/labs/klasy2.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