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:klasy2 [2017/03/28 01:50]
mwp [Klasy i obiekty II]
pl:dydaktyka:jimp2:2017:labs:klasy2 [2019/06/27 15:50] (aktualna)
Linia 1: Linia 1:
 ====== Klasy i obiekty II ====== ====== Klasy i obiekty II ======
  
-===== Zasady ​żądzące życiem obiektu =====+===== Zasady ​rządzące życiem obiektu =====
  
 Zapoznać się z regułami przedstawionymi w dokumentacji na stacku 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]].+[[http://www.riptutorial.com/cplusplus/topic/​1206/​the-rule-of-three--five--and-zero|Zasada zera, zasada pięciu, zasada trzech]].
  
  
 <file cpp XXX.h> <file cpp XXX.h>
 +#include <​string>​
 +#include <​cstring>​
 +#include <​algorithm>​
 +using ::​std::​swap;​
 +
 class XXX { class XXX {
 +public:
   //w zeszłym odcinku:   //w zeszłym odcinku:
   //domyślny konstruktor   //domyślny konstruktor
Linia 27: Linia 33:
   //5. Destruktor   //5. Destruktor
   ~XXX();   ~XXX();
-}+};
 </​file>​ </​file>​
  
Linia 51: Linia 57:
   //tutaj przypisujemy stan obiektu jednego do drugiego   //tutaj przypisujemy stan obiektu jednego do drugiego
   //ale obydwa są już zaincjalizowane...   //ale obydwa są już zaincjalizowane...
-  ​XXX another_xxx = new_xxx;+  another_xxx = new_xxx;
   ​   ​
   //tutaj kończy się zakres funkcji main i wszystkie trzy obiekty tracą ważność   //tutaj kończy się zakres funkcji main i wszystkie trzy obiekty tracą ważność
Linia 60: Linia 66:
 ==== A co z przenoszącymi wersjami z C++11 ==== ==== 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...+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 ​zasobów trzymanych w obiektach ​pozostaje stała...
  
 Przykład: Przykład:
 <file cpp main.cpp>​ <file cpp main.cpp>​
-int movement_example() {+ 
 +XXX make_copy(XXX xxx) { 
 +  return xxx; 
 +
 + 
 +int main() {
   //​Konstrukcja obiektu za pomocą domyślnego konstruktora   //​Konstrukcja obiektu za pomocą domyślnego konstruktora
   //​nieciekawe   //​nieciekawe
Linia 79: Linia 90:
   //ale obydwa są już zaincjalizowane...   //ale obydwa są już zaincjalizowane...
   //stan new_xxx teraz będzie w another_xxx,​ a sam new_xxx jest niszczony   //stan new_xxx teraz będzie w another_xxx,​ a sam new_xxx jest niszczony
-  ​XXX another_xxx = move(new_xxx);​+  another_xxx = std::move(new_xxx);​
   ​   ​
 +  //tutaj zostanie wywoały konstruktor kopiujący (argument wysyłany przez wartość)
 +  //a następnie konstruktor przenoszący (bo wartość tymczasowa wytworzona przez funkcję make_copy()
 +  //i tak zaraz zginie
 +  XXX one_more = make_copy(another_xxx);​
 +  //tutaj watość tymczasowa z funkcji make_copy już jest niszczona i wywoływany jest destruktor
 +  ​
 +  //...
 +
   //tutaj kończy się zakres funkcji main i wszystkie trzy obiekty tracą ważność   //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)   //zostają wywołane więc destruktory (na wszystkich trzech, ale tylko jeden ma ważny stan)
 } }
 </​file>​ </​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 chwilikiedy klasy posiadają pola dynamicznie alokowaneKonstruktor kopiujący **musi** przyjmować jako parametr referencje ​do obiektu. Przykład konstruktora kopiującego dla klasy Punkt poprzednich laboratoriów (nie jest on w tej klasie wymagany)Deklaracja:<code cpp>..+==== Co jest tymi zasobami? ==== 
-Punkt(const Punkt&)+ 
-...</code+Zasoby to wszystko co musi być najpierw zainicjalizowanea następnie zniczone, ale nie przy użyciu konstruktora i destruktora. 
-Definicja:<code cpp>... +Np. pamięć jako surowy wskaźnik (inicjalizacja new, zwalnianie delete), uchwyt ​do pliku (open, close), połączenie ​bazą danych, itp...  
-Punkt::Punkt(const Punkt &punkt){ + 
-this->x = punkt.x; +Jeśli obiekt ​nie posiada żadnych zasobów tylko składa się z typów prostych i kontenerów biblitecznych to można wykorzystać **regułę 0** i polegać na powyższych funkcjach automatycznie wygenerowanych przez kompilator. 
-this->y = punkt.y; + 
-cout << "​Konstruktor kopiujący!"​ << endl;+Jeśli jednak klasa **XXX** wygląda tak: 
 +<file cpp XXX.h> 
 +class XXX { 
 + ​private:​ 
 +  char *name_; 
 +}
 +</file
 +<file cpp XXX.cpp> 
 +XXX::XXX() : name_{new char[1024]} ​
 +  
 } }
-...+</​file>​ 
 + 
 +Brak destruktora powoduje wyciek pamięci, która nigdy nie jest zwalniana:​ 
 +<file cpp XXX.cpp> 
 +//5destruktor:​ 
 +XXX::~XXX() { 
 +  delete [] name_; 
 +
 +</​file>​ 
 + 
 +Brak konstruktora może teraz doprowadzić do tragedii wielokrotnej próby zwolnienia tej samej pamięci: 
 +<file cpp main.cpp> 
 +int main() { 
 +  XXX old; 
 +  XXX new_xxx{old};​ 
 +  //teraz zarówno old jak i new_xxx pokazują na ten sam fragment pamięci 
 +  //... niedobrze bo zaraz zostanie uruchominy destruktor pierwsego i drugiego obiektu 
 +  //i drugi destruktor będzie chciał zwolnić drugi raz ten sam fragment pamięci... 
 +
 +</​file>​ 
 + 
 +Więc: 
 +<file cpp XXX.cpp>​ 
 +//​konstruktor kopiujący:​ 
 +XXX::​XXX(const XXX& xxx) { 
 +    size_t sz = strlen(xxx.name_);​ 
 +    name_ = new char[sz]; 
 +    strcpy(name_,​xxx.name_);​ 
 +    //Teraz nowy obiekt pokazuje na nowy fragment pamięci,  
 +    //ale ze skopiowaną informacją 
 +  } 
 +  //operator przypisania:​ 
 +XXX & XXX::​operator=(const XXX& xxx) { 
 +    //jeśli ktoś wpadł na pomysł x = x; 
 +    if (this == &xxx) { 
 +      return *this; 
 +    } 
 +    //w przyciwynym wypadku mamy x = y; 
 +    //musimy sami zwolnic pamięć po x (czyli this): 
 +    delete[] name_; 
 +    //i wreszcie kopiowanie, ten kod jest 
 +    //jest identyczny więc można by go wydzielić do innej metody... 
 +    size_t sz = strlen(xxx.name_);​ 
 +    name_ = new char[sz]; 
 +    strcpy(name_,​xxx.name_);​ 
 +  } 
 +</​file>​ 
 + 
 +I ostatnia poprawka: 
 + 
 +<file cpp XXX.cpp>​ 
 +  //​konstruktor przenoszący:​ 
 +XXX::​XXX(XXX &&​xxx) : name_{nullptr} { 
 +    swap(name_,​xxx.name_);​ 
 +    //Bardzo popularna szutczka 
 +    //wiemy, ze za chwilę xxx zostanie zniszczony  
 +    //za pomocą destrukotra,​ więc inicjalizujemy 
 +    //this na nullptr i wymieniamy się z xxx 
 +    //delete nullptr jest bezpieczna operacją i nic się nie stanie... 
 +  } 
 + 
 +//operator przenoszący:​ 
 +XXX & XXX::​operator=(XXX &&​xxx) { 
 +    //jeśli ktoś wpadł na pomsył x = move(x); 
 +    if (this == &xxx) { 
 +      return xxx; 
 +    } 
 +    //w przyciwynym wypadku mamy x = move(y); 
 +    //musimy sami zwolnic pamięć po x (czyli this): 
 +    delete[] name_; 
 +    //i wreszcie przenosimy stan, ten kod jest 
 +    //jest identyczny więc można by go wydzielić do innej metody... 
 +    name_ = nullptr; 
 +    swap(name_,​xxx.name_);​ 
 +  } 
 +</​file>​ 
 + 
 +==== Usuwanie domyślnie generowanych funkcji ==== 
 + 
 +Usuwanie domyślnie generowanych funkcji w C%%++%%11 jest wyjątkowo prostym zadaniem wystarczy zadeklarować metodę a następnie przypisać jest **delete**. 
 + 
 +<code cpp> 
 +class Y { 
 +  Y(Y &&​y) = delete; 
 +  Y &​operator=(Y &&​y) = delete; 
 +};
 </​code>​ </​code>​
 +W tym momencie nie będzie możliwe przenoszenie obiektów klasy Y. W przypadku zwracania obiektów Y z funkcji kompilator zrezygnuje z optymalizacji przeniesienia i zastosuje kopiujące odpowiedniki metod.
 +===== Konstruktory z listą inicjalizacyjną =====
  
-Konstruktor kopiujący jest wywoływany automatycznie w następujących sytuacjach+Istnieje jeszcze specjalny typ konstruktora przyjmujący listę inicjalizacyjną, przydatny przy inicjalizacji własnych kontenerów. 
-  ​* Podczas przekazywania obiektów przez wartość do funkcji ​(tworzone są ich lokalne kopie), +<file cpp Counter.h>​class Counter { 
-  ​* Podczas zwracania obiektów przez funkcje, ​ + ​public:  
-  ​* Podczas deklaracji obiektu gdy jako parametr konstruktora podany zostanie inny obiekt. +  ​//strasznie długi typ... 
-  ​* **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>... +  Counter(const std::​initializer_list<​std::​pair<​const std::​string,​ unsigned int>> &​elements: counter_{elements} { 
-Punkt p(12,34)+   
-Punkt p2(p)  // Konstruktor kopiujący. +  ​
-Punkt p3 = p;  ​// Konstruktor kopiujący. + ​private:​ 
-Punkt p4;+  ​std::​map<​std::​stringunsigned intcounter_
 +}
 +</file>
  
-//NIE ZADZIAŁA KONSTRUKTOR KOPIUJĄCY +Teraz można zainicjalizować obiekt Counter jako: 
-//TYLKO DOMYŚLNY OPERATOR PRZYPISANIA + 
-p4 = p2;+<code cpp> 
 +Counter cnt {{"​abc",​ 14}, {"​efg",​ 17}, {"​hij",​0}};
 </​code>​ </​code>​
  
Linia 121: Linia 237:
 class Matrix{ class Matrix{
   ...   ...
-  void wyswietl() const;+  void Print() const;
   ...   ...
 }; };
Linia 131: Linia 247:
 //Metoda ustawiająca wartość w komórce macierzy o indeksie (1,1) //Metoda ustawiająca wartość w komórce macierzy o indeksie (1,1)
 //​Wywołanie takiej metody wygeneruje błąd podczas kompilacji. //​Wywołanie takiej metody wygeneruje błąd podczas kompilacji.
-m.set(1,​1,​12);  ​+m.Set(1,​1,​12);  ​
  
 //Gdyby metoda wyświetl nie była zadeklarowana jako const //Gdyby metoda wyświetl nie była zadeklarowana jako const
 //takie wywołanie też spowodowałoby błąd //takie wywołanie też spowodowałoby błąd
-m.wyswietl();+m.Print();
 </​code>​ </​code>​
  
Linia 232: Linia 348:
  
 ====== Ćwiczenia ====== ====== Ćwiczenia ======
-  - **[1 punktDopisz konstruktor kopiujący do klasy Punkt poprzedniego laboratorium ​i prześledź jak wyglądają wywołania konstruktorówZobacz, ​że przypisanie obiektów poza deklaracją nie wywołuje konstruktora kopiującego.** +  - [2 plusyPrzepisać program z klasą XXX treści laboratoriów ​i prześledzić jego działanie w debuggerzeUstawić pułapki debuggera na wszystkich 5 specjalnych funkcjach jak i przy funkcji main() następnie używając pracy krokowej (F8,F7,F9...) prześledzić zachowanie kodu 
-  - **[1 punktSprawdź 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 zwróconej DTab będziemy chcieli zmienić jakieś elementy, albo ją rozszerzyć?​ Dopisz ​do klasy konstruktor kopiujący.** +  - [3 plusyAlokacja dużych bloków pamięci na raz jest szybszym sposobem zarządzania pamięcią jeśli tylko mamy odpowiednie przesłanki profilu wykorzystania pamięci. Ale tym razem problem jest znacznie prostszy należy jedynie przygotować klasę MemoryChunk z konstruktorem parametrycznym przyjmującym rozmiar bloku pamięci do zaalokowaniaKlasa powinna ​przechowywać uchwyt do bloku pamięci w postaci zwykłego wskaźnika na **int8_t** alokowanego przez operator **new** (żeby nie było za prosto) 
-  - {{ .:​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 danychzwł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ę dwóch elementów (patrz rysunek obok)+    * Moduł: **memorychunk** 
-    * 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. +    * Pliki z implementacją:​ **MemoryChunk.h/​cpp** 
-    * Elementów składowych listy, tzwWęzłów, z których każdy posiada pole przechowujące dane i pole będące wskaźnikiem ​do następnego węzłaObiekt 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 ​konstruktorze kopiującym__ :!:+    * Używana struktura danych: **MemoryChunk** 
 +    * Sygnatury metod w klasie MemoryChunk: ​<code cpp> ​ Rule of Five 
 +  MemoryChunk(size_t sz)
 +  int8_t *MemoryAt(size_t offset) const; 
 +  size_t ChunkSize() const;</​code>​ 
 +    ​Przestrzeń nazw: **memorychunk** 
 +    * Importy: <code cpp>#​include <​cstdint>​ 
 +#include <​algorithm></​code>​ 
 +    * Przydatne metody: <code cpp>​std::​copy(from,​from+size,​to);​ 
 +</​code>​ 
 +  - **[2 punkty] Pula łańcuchów znaków jest to jeden ze sposobów optymalizacji dużej ilości napisów w programie. Trick polega na tym, że powtarzające ​się napisy przechowujemy ​tym samym miejscu pamięci. Obiektowi puli napisów można powierzyć przechowywanie konkretnego napisu za pomocą funkcji Intern w rezultacie dostajemy uchwyt ​do napisu, który jest już zarządzany przez obiekt puliJeśli kilka identycznych napisów zostanie wstawionych do puli to tylko jedna instancja napisu będzie przechowywana wewnątrz, a wszystkie zwracany uchwyty pokazują ciągle na ten sam napis wewnątrz puliNiestety klasa string ​nie nadaje się do naszych celów zwracania uchwytu napisuponieważ to ona sama zarządza pamięcią i wymaga kopiowania napisu a ponadto pozwalałaby na modyfikowanie napisów przechowywanych w strukturze (jeśli ​kilka napisów wskazuje ​na ten sam obszar ​pamięci ​to byłoby straszne). W C%%++%%17 wchodzi typ string_view czyli niezmienny napisktóry dodatkowo posiada metodę substr działającą w czasie stałym! Twoim zadaniem jest przygotowanie implementacji takie puli napisów.** 
 +    * Moduł: **textpool** 
 +    * Pliki implementacją**TextPool.h/​cpp** 
 +    * Używana struktura danych: **TextPool** 
 +    * Sygnatury metod w klasie TextPool: <code cpp> ​ Rule of Five (z usuniętą możliwością kopiowania) 
 +  domyślny konstruktor 
 +  konstruktor z listą inicjalizacyjną 
 +  std::​experimental::​string_view Intern(const std::string &str)
 +  size_t StoredStringCount() const;</​code>​ 
 +    * Przestrzeń nazw: **pool** 
 +    * Importy: <code cpp>#​include <​initializer_list>​ 
 +#include <​string>​ 
 +#include <​experimental/​string_view>​ 
 +#include <​set></​code>​ 
 +    * Przykład użycia: <code cpp>#​include <​iostream>​ 
 +#include "​TextPool.h" 
 + 
 +using namespace pool; 
 +using namespace std; 
 + 
 +int main() { 
 +  //​Inicjalizacja wstępna puli za pomocą listy inicjalizacyjnej 
 +  TextPool pool {"​abc",​ "​efg",​ "​hij",​ "​klmn",​ "​oprst"​};​ 
 + 
 +  //​wstawienie napisu ​do puli 
 +  auto s1 = pool.Intern("​efg"​)
 + 
 +  ​//wstawienie kolejnego napisu do puli (w obu przypadkach nie 
 +  ​//powinien ​się zmienić rozmiar puli) 
 +  auto s2 = pool.Intern("efg"); 
 + 
 +  cout << (s1 == s2 ? "​True"​ : "​False"​) << endl; //uchwyty są tymi samymi napisami co do wartości 
 +  cout << pool.StoredStringCount() << endl; // puli jest wciąż 5 napisów 
 +  cout << (s1.begin() == s2.begin() ? "​True"​ : "​False"​) << endl; //na dodatek uchywyty s1 s2 pokazują dokładnie na ten sam napis w puli (wskaźniki są identyczne) 
 +}</​code>​
   - [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.   - [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 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:**   - **[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>​+    * Moduł: **matrix** 
 +    * Pliki z implementacją:​ **Matrix.h/​cpp** 
 +    * Używana struktura danych: **Matrix** 
 +  ​* Klasa powinna wewnętrznie reprezentować macierz przy pomocy tablicy dwuwymiarowej obiektów typu std::​complex<​double>​. 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 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 pozwalające na ustawianie/​pobieranie jej elementów
Linia 275: Linia 438:
  
 <WRAP center round info 60%> <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]].+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**. ​Więcej nich dowiemy ​się na laboratorium [[.:​wyjatki|Wyjątki]].
 </​WRAP>​ </​WRAP>​
  
  
pl/dydaktyka/jimp2/2017/labs/klasy2.1490658651.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