Both sides previous revision
Poprzednia wersja
Nowa wersja
|
Poprzednia wersja
|
pl:dydaktyka:jimp2:2017:labs:klasy2 [2017/03/28 10:20] kkutt [Co jest tymi zasobami?] |
pl:dydaktyka:jimp2:2017:labs:klasy2 [2019/06/27 15:50] (aktualna) |
====== 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 |
//5. Destruktor | //5. Destruktor |
~XXX(); | ~XXX(); |
} | }; |
</file> | </file> |
| |
//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ść |
} | } |
| |
int movement_example() { | int main() { |
//Konstrukcja obiektu za pomocą domyślnego konstruktora | //Konstrukcja obiektu za pomocą domyślnego konstruktora |
//nieciekawe | //nieciekawe |
//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ść) | //tutaj zostanie wywoały konstruktor kopiujący (argument wysyłany przez wartość) |
<file cpp XXX.h> | <file cpp XXX.h> |
class XXX { | class XXX { |
X() : name_{new char[1024]} { | |
| |
} | |
private: | private: |
char *name_; | char *name_; |
}; | }; |
| </file> |
| <file cpp XXX.cpp> |
| XXX::XXX() : name_{new char[1024]} { |
| |
| } |
</file> | </file> |
| |
Brak destruktora powoduje wyciek pamięci, która nigdy nie jest zwalniana: | Brak destruktora powoduje wyciek pamięci, która nigdy nie jest zwalniana: |
<file cpp XXX.cpp> | <file cpp XXX.cpp> |
class XXX { | //5. destruktor: |
//... | XXX::~XXX() { |
//5. destruktor: | delete [] name_; |
~XXX() { | } |
delete [] name_; | |
} | |
</file> | </file> |
| |
Więc: | Więc: |
<file cpp XXX.cpp> | <file cpp XXX.cpp> |
class XXX { | //konstruktor kopiujący: |
//... | XXX::XXX(const XXX& xxx) { |
//konstruktor kopiujący: | |
XXX(const XXX& xxx) { | |
size_t sz = strlen(xxx.name_); | size_t sz = strlen(xxx.name_); |
name_ = new char[sz]; | name_ = new char[sz]; |
strcpy(name_,xxx.name); | strcpy(name_,xxx.name_); |
//Teraz nowy obiekt pokazuje na nowy fragment pamięci, | //Teraz nowy obiekt pokazuje na nowy fragment pamięci, |
//ale ze skopiowaną informacją | //ale ze skopiowaną informacją |
} | } |
//operator przypisania: | //operator przypisania: |
XXX & operator=(const XXX& xxx) { | XXX & XXX::operator=(const XXX& xxx) { |
//jeśli ktoś wpadł na pomsył x = x; | //jeśli ktoś wpadł na pomysł x = x; |
if (this == &xxx) { | if (this == &xxx) { |
return xxx; | return *this; |
} | } |
//w przyciwynym wypadku mamy x = y; | //w przyciwynym wypadku mamy x = y; |
//i wreszcie kopiowanie, ten kod jest | //i wreszcie kopiowanie, ten kod jest |
//jest identyczny więc można by go wydzielić do innej metody... | //jest identyczny więc można by go wydzielić do innej metody... |
size_t sz = strlen(xxx.name); | size_t sz = strlen(xxx.name_); |
name = new char[sz]; | name_ = new char[sz]; |
strcpy(name,xxx.name); | strcpy(name_,xxx.name_); |
} | } |
</file> | </file> |
| |
<file cpp XXX.cpp> | <file cpp XXX.cpp> |
class XXX { | |
//... | |
//konstruktor przenoszący: | //konstruktor przenoszący: |
XXX(XXX &&xxx) : name_{nullptr} { | XXX::XXX(XXX &&xxx) : name_{nullptr} { |
swap(name_,xxx.name_); | swap(name_,xxx.name_); |
//Bardzo popularna szutczka | //Bardzo popularna szutczka |
//delete nullptr jest bezpieczna operacją i nic się nie stanie... | //delete nullptr jest bezpieczna operacją i nic się nie stanie... |
} | } |
//operator przenoszący: | |
XXX & operator=(XXX &&xxx) { | //operator przenoszący: |
| XXX & XXX::operator=(XXX &&xxx) { |
//jeśli ktoś wpadł na pomsył x = move(x); | //jeśli ktoś wpadł na pomsył x = move(x); |
if (this == &xxx) { | if (this == &xxx) { |
* Pliki z implementacją: **MemoryChunk.h/cpp** | * Pliki z implementacją: **MemoryChunk.h/cpp** |
* Używana struktura danych: **MemoryChunk** | * Używana struktura danych: **MemoryChunk** |
* Sygnatury metod w klasie SimpleUrl: <code cpp> Rule of Five | * Sygnatury metod w klasie MemoryChunk: <code cpp> Rule of Five |
MemoryChunk(size_t sz); | MemoryChunk(size_t sz); |
int8_t *MemoryAt(size_t offset) const; | int8_t *MemoryAt(size_t offset) const; |
size_t ChunkSize() const;</code> | size_t ChunkSize() const;</code> |
* Przestrzeń nazw: **memory** | * Przestrzeń nazw: **memorychunk** |
* Importy: <code cpp>#include <cstdint> | * Importy: <code cpp>#include <cstdint> |
#include <algorithm></code> | #include <algorithm></code> |
* Przydatne metody: <code cpp>std::copy(from,from+size,to); | * Przydatne metody: <code cpp>std::copy(from,from+size,to); |
</code> | </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 przechwujemy w tym samym miejscu pamięci. Niestety klasa string się nie nadaje do naszych celów, gdyż pozwalałaby na modyfikowanie napisów, przechowywanych w strukturze (a 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 napis, który dodatkowo posiada metodę substr działającą w czasie stałym!** | - **[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 w 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 puli. Jeś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 puli. Niestety klasa string nie nadaje się do naszych celów zwracania uchwytu napisu, ponieważ 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 napis, który dodatkowo posiada metodę substr działającą w czasie stałym! Twoim zadaniem jest przygotowanie implementacji takie puli napisów.** |
* Moduł: **textpool** | * Moduł: **textpool** |
* Pliki z implementacją: **TextPool.h/cpp** | * Pliki z implementacją: **TextPool.h/cpp** |
#include <experimental/string_view> | #include <experimental/string_view> |
#include <set></code> | #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; // w puli jest wciąż 5 napisów |
| cout << (s1.begin() == s2.begin() ? "True" : "False") << endl; //na dodatek uchywyty s1 i 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:** |
| * 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 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> |
| |
<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 o nich dowiemy się na laboratorium [[.:wyjatki|Wyjątki]]. |
</WRAP> | </WRAP> |
| |
| |