====== Podstawy programowania 2 ====== ===== Wykłady ===== Wykłady odbywają się w środy o 19:00 na platformie MS Teams. [[https://teams.microsoft.com/l/team/19%3AVB_hmfafz560jilREju7ucJGcBPFfGcOUJcotJ6lNKQ1%40thread.tacv2/conversations?groupId=feffb47e-61c4-4e7f-a59c-8e7da40d7929&tenantId=80b1033f-21e0-4a82-bbc0-f05fdccd3bc8|Link do zespołu]] Kod do zespołu zostanie przekazany staroście -{{ ::01-isi-cpp-intro-wsk.pdf | Wprowadzenie. Wskaźniki - przypomnienie}} [>>28.02.2024] -{{ ::02-isi-cpp-ref-skladow-klas.pdf | Referencje, składowe klas. Wprowadzenie do standardowej biblioteki}} [06.03.2024, 13.03.2024] -{{ :03-isi-cpp-kompozycja-dziedziczenie.pdf |Kompozycja i dziedziczenie}} [>>20.03.2024] -{{ ::04-isi-cpp-funkcje-wirtualne.pdf |Funkcje wirtualne (polimorfizm)}} [] -{{ ::05-isi-cpp-konstruktory-destruktory.pdf |Konstruktory, destruktory}} [05.04.2023] -{{ ::06-isi-cpp-przeciazanie-fun-op.pdf |Przeciążanie funkcji i operatorów}} [12.04.2023, 26.04.2023] -{{ ::07-isi-cpp-cast-rtti.pdf |Operatory rzutowania w C++}} [26.04.2023] -{{ ::08-isi-cpp-exceptions.pdf| Wyjątki}} [10.05.2023] -{{ ::09-isi-cpp-kontenery.pdf | Kontenery i iteratory}} [10.05.2023(wektor) + ] -{{ ::10-isi-cpp-przenoszenie.pdf | Semantyka przenoszenia}} [%30.05.2022] -{{ ::11-isi-cpp-szablony.pdf | Wielokrotne użycie, szablony}} [%06.06.2022] -{{ ::12-isi-cpp-stl.pdf | Standardowa biblioteka. Kontenery, algorytmy, wyrażenia lambda}} [>> 07.06.2021,14.06.2021] ===== Egzamin ===== * I termin 10.07.2023 15:30-17:30 sala H24 w B1 * II termin 16.07.2023 14:00-16:00 sala C2 224 * III termin 05.09.2023, 12:00-14:00 C2 431 Podczas egzaminu: * można korzystać z dowolnych materiałów w formie fizycznej (podręczników, notatek, itp.) * nie można wymieniać się materiałami, podawać notatek sąsiadom, itp. * nie można korzystać z telefonów Proszę przynieść własne kartki formatu A4, np. 3 na odpowiedzi oraz 3 na brudnopis i 2 długopisy. ==== Uwagi 2023 ==== *Nie trzeba pisać trg.a=towary[i].a; trg.b=towary[i].b; trg.c=towary[i].c; trg.d=towary[i].d; Wystarczy ''trg=towary[i]'' ==== Uwagi ==== === Uwaga 1 === Jeżeli tematem było napisanie szablonów klas typu ''Set'' lub ''Wielozbiór'' z zastrzeżeniami dotyczącymi dostępnych operatorów dla klasy będącej parameterem szablonu, to zapewne nie można było użyć ''std::set'' lub ''std::map''. === Uwaga 2 === Szablony ''unordered_set'' lub ''unordered_map'' działają na klasach "haszowalnych", tzn. takich dla których da się obliczyć wartość indeksu w tablicy na podstawie zawartości. Proszę spróbować skompilować: class A{ public: A(int _v):v(_v){} int v; bool operator==(const A&a)const{ return v==a.v; } }; int main(){ std::unordered_set set; set.insert(A(1)); std::cout<<(set.find(A(1))!=set.end())< Dla nietypowej własnej klasy należy dostarczyć własny obiekt funkcyjny do obliczania "hasza" (czyli położenia w tablicy)... class A{ public: A(int _v):v(_v){} int v; bool operator==(const A&a)const{ return v==a.v; } }; class MyHashForA{ public: size_t operator()(const A&a)const{ return std::hash{}(a.v); } }; int main(){ std::unordered_set set; set.insert(A(1)); std::cout<<(set.find(A(1))!=set.end())< === Uwaga 3 === Funkcja nie może zwracać wskaźnika po dereferencji do obiektu, dla którego pamięć przydzielono na stercie. Jak zwolnić tę pamięć? Memory leak... class A{ public: int v; }; A bardzo_zle_napisana_funkcja(){ A*ptr = new A(); ptr->v=10; printf("ptr=%p\n",ptr); return *ptr; } int main(){ A a1 = bardzo_zle_napisana_funkcja(); printf("&a1=%p\n",&a1); A a2; a2=bardzo_zle_napisana_funkcja(); printf("&a2=%p\n",&a2); // jak zwolnić ptr? } // Wynik ptr=0x80004add0 &a1=0xffffcc3c ptr=0x80004ae40 &a2=0xffffcc38 ==== Planowane zadania ==== * **Bez STL** Podana zostanie nazwa klasy, ogólny typ implementacji i metody/funkcje do zaimplementowania. Należy zadeklarować klasę wraz z metodami oraz podać implementacje metod. Nie używamy biblioteki standardowej. Student podejmuje decyzje o tym: * jak nazywają się atrybuty * jakie atrybuty będą potrzebne * czy implementować dodatkowe metody (prywatne, chronione) takie jak free(), copy() lub move() * **Użyj STL** Podana zostanie nazwa klasy , ogólny typ implementacji i metody/funkcje. Z reguły do składowania danych będzie użyty kontener standardowej biblioteki. Analogicznie, student podejmuje decyzję o szczegółach implementacji, w tym nazwach i typach atrybutów oraz metodach pomocniczych. * **Co się wypisze i dlaczego** Podany zostanie kod zawierajacy deklaracje klas, obiektów (globalnych) i lokalnych w funkcji main. Podczas wykonania poszczególne instrukcje będą odpowiadały za wypisywanie tekstów na ekranie. Odpowiadając na pytanie zdalnie można wprowadzić kod do edytora i zaobserwować wynik - chociaż nie wiadomo, czy to się czasowo opłaca. Dlatego szczególny nacisk będzie położony na **dlaczego**. Typowe elementy do rozważenia: * wywołanie funkcji wirtualnych w konstruktorach i destruktorach * obecność wirtualnych/niewirtualnych destruktorów * wyjątki w konstruktorach oraz zwalnianie stosu podczas obsługi wyjątków * **Zadanie praktyczne** Niewielki program typu odczyt ze standardowego wejścia i pisanie na standardowe wyjście. Można użyć standardowej biblioteki. Szacowany na 15 minut. ==== Zasady oceny ==== *Kod w komentarzu nie będzie sprawdzany *W przypadku wpisania dwóch wersji (np. tej samej metody) będzie brana pod uwagę pierwsza z nich ==== Za co będą odliczane punkty ==== ** Bez STL ** * Za użycie kontenerów STL * Za własne implementacje funkcji z (strlen, strcpy, strcat, itp.) * Za nadmiarowe alokacje pamięci, np. aby przedłużyć tablicę wskazywana przez start: * tworzona jest tablica tmp o rozmiarze takim, jak start * zawartość start jest kopiowana do tmp * zawartość start jest usuwana * następuje alokacja pamięci start = new T[nowa długość] * zawartość tmp kopiowana jest do start * tmp jest usuwana * Za użycie VLA * Za przedłużanie wektora o jedno miejsce przy dodawaniu elementów zamiast przedłużania go o większą liczbę elementów * Za szukanie końca listy, zamiast użycia wskaźnika na koniec listy -- dodanie elementu ma złożoność O(n) zamiast O(1) * Za użycie/implementację funkcji typu: 'zwróć i-ty element listy' -- iteracja ma złożoność O(n^2) zamiast O(n) ** Użyj STL ** * Za umieszczanie w zbiorze ''set'' elementów, które nie mogą być w nim umieszczone z powodu braku możliwości ich porównywania * Za iterację po zbiorze ''set'', aby sprawdzić czy zawiera element - O(n) zamiast O(log n) * Za niezwalnianie pamięci, jeżeli kontener zawiera wskaźniki do obiektów, dla których zaalokowano pamięć na stercie ===== Laboratoria, grupy 2a i 2b ===== :!: Proszę zatwierdzać test. Jest na to 10 minut po upływie czasu ==== Laboratorium 11 ==== sizeof(text) ma wartość 8 (na platformie 64-bitowej). Długość tekstu obliczamy za pomocą strlen() TextListElement::TextListElement(const char *txt) { value = new char[sizeof(txt)]; strcpy(value, txt); next = 0; prev = 0; } ==== Lab 10 Dirent ==== * get_path() to sklejenie parent->get_path separatora i name. Używając parent->name przeskanujemy ''c:/'', ''c:/user'' ale nie zjedziemy do ''c:/user/kasia'' bo nasza ściezka będzie miała postać ''user/kasia'' * Pamiętamy o zabezpieczeniu operatora przypisania za pomocą &other != this Directory&Directory::operator=(const Directory&other){ if(&other!=this){ free(); copy(other); } return *this; } * musimy opróżnić wektor, bo zostaną //duchy// ;-) usuniętych obiektów void Directory::free(){ for (auto e:entries)delete e; entries.clear(); } * Wskaźniki do parent kopiownaych obiektów muszą należeć do nowej hierarchii. To jest niuans, ale ważny ze względu na spójność. void Directory::copy(const Directory&other){ this->name = other.name; // NIE: this->parent = other.parent; this->parent = nullptr; // Obiekt nardrzędny ustawi wskaźnik w instrukcji (2) //(1) kopiowanie for(auto e:other.entries){ // entries.push_back( kopia e) } // (2) for(auto e:entries){ e->parent=this; } } === Downcasting? === Poniższe rozwiązanie jest poprawne, ale dyskusyjne... Nieporzebnie stosowane jest rzutowanie w dół hierarchii. Klasa u szczytu hierarchii musiałaby znać wszystkie klasy potomne. Dirent *Dirent::find(const char *name) { if (is_dir()) { for (auto element : dynamic_cast(this)->entries) { if (element->name == name) { return element; } else if (element->is_dir()) { if (element->find(name)) { return element->find(name); } } } } } Zamiast tego (jeżeli decydujemy się na wprowadzenie do interfejsu klasy bazowej): // zadeklarowane jako virtual Dirent *Dirent::find(const char *_name){ if (name==_name ) return this; return nullptr; } Dirent *Directory::find(const char *_name) { if (name==_name ) return this; // lub: //auto e = Dirent::find(_name); //if(e) return e; for (auto element : entries) { auto e = e->find(_name); if(e)return e; } return nullptr;//0 } Po co downcasting w przypadku funkcji wirtualnych? for (auto& e:entries) { if(e->is_dir()) dynamic_cast(e)->list(os, indent); if(e->is_file()) dynamic_cast(e)->list(os, indent); } ==== Lab 9 - String ==== * Zawsze zadaj sobie następujące pytania: * Czy jest miejsce na 0 na końcu? * Czy dopisano 0 na końcu * Czy przy sklejaniu dwóch tekstów a i b jest pojemność jest wystarczająca, czyli >= strlen(a)+strlen(b)+1. Jeżeli dopuszczamy zerowe wskaźniki to: ''capacity > size()+strlen(_txt)'' lub ''capacity > size()+other.size()'' * Funkcje biblioteczne, np.: ''strlen()'', nie działają dla zerowych wskaźników. [[https://stackoverflow.com/questions/5796103/strlen-not-checking-for-null|Dyskusja]] * Używamy ''strcat()'' i ''strcpy()''. Te funkcje przynajmniej nie zapomną o zerze na końcu! * Nie powtarzamy kodu funkcji bibliotecznych, bo szkoda na to czasu - zwłaszcza podczas egzaminu * Porównujemy zawartość, a nie adresy (które na ogół są zawsze różne). bool String::operator==(const char*_txt)const{ if(_txt == this->txt) return true; // BŁĄD return false; } * Nie mieszamy malloc i free [[http://home.agh.edu.pl/~pszwed/wiki/lib/exe/fetch.php?media=08-imperatywne-jezyk-c-dynamiczna-alokacja-pamieci.pdf|strona 10]] * Implementując ''operator='' pamiętamy o ''if(&other!=this)'' - za to są odliczane punkty na egzaminie * VLA nie należą do standardu C++, patrz: [[http://home.agh.edu.pl/~pszwed/wiki/lib/exe/fetch.php?media=05-imperatywne-jezyk-c-deklaracje-typy.pdf|VLA]] oraz [[http://home.agh.edu.pl/~pszwed/wiki/lib/exe/fetch.php?media=08-imperatywne-jezyk-c-dynamiczna-alokacja-pamieci.pdf|dynamiczna alokacja pamięci, strona 18]]. Zajrzyj na [[https://stackoverflow.com/questions/1887097/why-arent-variable-length-arrays-part-of-the-c-standard|Stack overflow]]. Nie stosujemy VLA zamiast alokacji pamięci, po to aby finalnie zaalokować pamięć. * Jeżeli mamy operator inplace, np. +=, to nie powtarzamy jego kodu, szybciej (a także poprawnie, jeżeli += jest dobrze zaimplementowany) będzie: String operator+(Arg arg){ String ret(*this); ret += arg; return ret; } ==== Lab 7 Relacja === * iteracja po domain nie wystarcza. Czy ta relacja jest rzeczywiście zwrotna $\{(1,2),(1,1)\}$ * przy tego typu implementacjach należało skleic domain i range, nawet w najprostszy spsób (oczywiście można uzyć ''set_union'' lub ''insert(other.begin(),other.end())''): auto domain = get_domain(); auto range = get_range(); set all(domain); for(auto e:range)all.insert(e); *iteracja jak poniżej jest bez sensu... Naprawdę musimy sprawdzać, czy i-ty element tablicy jest w tablicy? for(int i=0;i *preferowane są rozwiązania proste i czytelne, na przykład bool relation::is_antisymmetric()const{ for(auto&e:pairs){ if(e.x!=e.y && has_pair(e.y,e.x))return false; } return true; } * one ułatwiają ewentualną zmianę struktur danych. Proszę wypróbować class relation{ public: class pair{ public: int x; int y; bool operator<(const pair&other)const; pair(int _x, int _y):x(_x),y(_y){} }; public: #if defined USE_VECTOR vector pairs; #endif #if defined USE_SET set pairs; #endif ... itd dwie zmiany: bool relation::has_pair(int x,int y)const{ #if defined USE_VECTOR for(int i=0;i ** Przy zmianie struktur danych żadna inna funkcja nie wymagała modyfikacji ** ==== Lab 5/6 Kretowisko === - [[lab_kretowisko_ii|Kretowisko II]] ==== Lab 4 ==== Po to napisaliśmy metodę Student::write, aby jej użyć, a nie pisać jak poniżej: void StudentList::write(ostream&os)const{ for(int i=0;i ==== Lab 2,3 ==== === Styl === * Proszę nie pisać w stylu: if(ok1){ if(ok2){ if(ok3){ // Tu dobry ciąg instrukcji... }else{ } }else{ } }else { } ... nikt nie sprawdzi tych warunków else * ** Metoda nie powinna wypisywać wiadomości na cout (chyba, że taka jest jej specyfikacja) ** Podczas testów możemy wypisywać informacje na cout === Algorytmy === U prawie wszystkich ''linspace()'' wygeneruje wyjątek dla n=1 === Funkcje === * do liczb zmiennoprzecinkowych używamy: fabs() * liczb double nie porównujemy obliczając ich różnicę ==== Lab 1 ==== **Logika zabezpieczeń** void Fibo1::init(int _n) { if(_n<=0)tab=0; n = _n; tab = new int[n]; } void Fibo1::destroy() { delete [] tab; } ArithmeticSequence(int _n, double _r, double _first) { if(_n<=0)return; // USTAW n=0, tab=0 n=_n; r=_r; first=_first; tab = new double[n]; } void Fibo1::init(int n) { if(n <= 0) { tab = 0; } tab = new int[n]; } void Fibo1::init(int _n) { n = _n; int* tab = new int[_n]; if(n <= 0){ tab = NULL; } } Fibo::~Fibo(){ if(n>0) delete []tab; // TO JEST POPRAWNE, ale proponuję raczej idiom if(tab) delete []tab; } **Class::member służy do czegoś innego...** for (int _i = 0; _i < ArithmeticSequence::n; _i++) { cout << ArithmeticSequence::arr[_i] << endl; } void Fibo1::fill() { tab[0]=1,tab[1]=1; // a jak tab==0 lub n=1? for(int i=2;i [[https://stackoverflow.com/questions/34614869/using-scope-operator-to-access-non-static-member-variables]] **Bez średnika!** ''#define N 10;'' **To nie jest stała, to jest VLA** Variable Length Arrays nie należą do standardu C++. Zamiast tego należy używać ''vector'' void fibo1(){ int n; int tab[n];