930 likes | 1.11k Views
Programov ání v C++. cvičení Filip Zavoral. Povinnosti k získání zápočtu. Doch á zka aktivní účast, znalost předchozí látky 3 nepřítomnosti OK, déledobější domluvit předem DÚ uprostřed semestru jedna větší domácí úloha Zápočtový program do 15.11. schválené zadání
E N D
Programování v C++ cvičení FilipZavoral
Povinnosti k získání zápočtu • Docházka • aktivní účast, znalost předchozí látky • 3 nepřítomnosti OK, déledobější domluvit předem • DÚ • uprostřed semestru jedna větší domácí úloha • Zápočtový program • do 15.11. schválené zadání • do 30.4. první pokus o odevzdání hotové verze • do konce LS komplet hotovo vč. doc (jinak neuznáno) • Zápočtový test • během zimního zkouškového období v labu • 3 termíny (3. termín během LS)
Můj první C++ program #include <iostream> int main() { std::cout << "Hello world" <<std::endl; return0; }
Můj první C++ program #include <iostream> int main() { std::cout << "Hello world" << std::endl; return 0; } ctrl-shift-B F5 ctrl-F5 F10 F11 F9 Debug / Window Watch, Auto, Locals Call Stack VS 2012 File / New / Project Installed / Templates / VC++ / Win32 Win32 Console Application Name, Location - OK !!! Application Settings Console Application vypnout! Precompiled header vypnout! SDL zapnout! Empty project Solution Explorer Solution / Project Source Files Add New/Existing Item Visual C++ / C++ File (.cpp) (Header Files)
Můj druhý program • Násobilka • funkce (třída, metoda) • parametr #include <iostream> int main() { std::cout << "Hello world" << std::endl; return 0; } void fnc( int x) { .... } int main() { fnc( 7); return 0; } Násobilka 7: 1 * 7 = 7 2 * 7 = 14 ... 10 * 7 = 70
Užitečné kousky kódu #include <iostream> #include <string> #include <vector> using namespace std; int delkaretezce( const string& s) { ... } void zpracuj( constvector<string>& a) { ... a[i] ... } int main( int argc, char ** argv) { vector<string> arg( argv, argv+argc); if ( arg.size() > 1 && arg[1] == "--help" ) { cout << "Usage: myprg [OPT]... [FILE]..."<< endl; return 8; } zpracuj( arg); return 0; } deklarace knihovních funkcí rozbalení prostoru jmen std předávání parametrů odkazem konstantní reference ! přístup k prvkům vectoru Solution Explorer / ProjectProperties / Config Properties Debugging / Command Arguments vektor pro komfortnější zpracování ošetření parametrů příkazové řádky výkonná funkce / metoda v mainunikdynicužitečného
Výpis parametrů #include <iostream> #include <string> #include <vector> using namespace std; void vypis( const vector<string>& a) { for( inti = 0; i < a.size(); ++i) { cout << "[" << a[ i] << "]"; } cout << endl; } int main( int argc, char ** argv) { vector<string> arg( argv, argv+argc); if ( arg.size() < 2) { cout << "Usage: myprgparameters"<< endl; return 8; } vypis( arg); return 0; } vypsat násobilku všech číselz parametrů příkazové řádky ... sinaprogramujtesami
Čísla a řetězce isdigit je lepší nepovinná referencena návratový parametr první nezkonvertovaný znak if( c >= '0' && c <= '9') if( isdigit( c)) int n = c - '0'; #include <string> int stoi ( s, size_t& idxRet = nullptr, int base = 10); stol, stoul, stoll, stof, stod, ... string to_string( val); OK - čísla jsou uspořádaná if( c >= 'a' && c <= 'z') konverzečísel a stringů písmena nejsou uspořádaná !! #include <sstream> int strtoint( const string& s) { stringstream ss(s); int n; ss >> n; return n; } #include <cctype> isalpha( c) isalnum(c) stream z řetězce num = num + c - 48; '0' ≉ 48
Třídy, objekty, metody class Pocitadlo{ public: Pocitadlo( void); ~Pocitadlo(); intpocet_pismen( void); private: intpocet_; boolve_slove_; }; deklarace třídy - .hpp konstruktor (defaultní), destruktor deklarace veřejných metod - rozhraní privátní data a metody - implementace #include <iostream> #include <cctype> #include "pocitadlo.hpp" int Pocitadlo::pocet_pismen( void) { char c; ... while(cin.get(c)) { if(ve_slove_) { if(isspace(c)) { ... } include deklarace definice metod - .cpp další typy, inicializace čtení po znacích typ znaku - asalpha, isdigit, isupper, ...
Tečka a čtyřtečka class Trida { intfce( int x); }; :: kvalifikátor nalevo vždy typ intTrida::fce( int x) { ... } { Tridaobjekt; objekt.fce( 1); } Tridaobjekt = new Trida(); . operátor přístupuk položceobjektu nalevo vždy proměnná
Inline a ne-inline metody s = ob.r inline metoda rozvinutí místo volání s = ob.r class Trida { std::stringgetResult () { return r; } std::string slozitaFce( int x); intjinaFce( int x) { int y = -1; for( i = 0; i < 10; ++i) .... } }; trida.h #include "trida.h" { Trida ob; string s; s = ob.getResult(); s = ob.slozitaFce( 1); int z = jinaFce( 2); } #include "trida.h" stringTrida::slozitaFce( int x) { int y; for( i = 0; i < 10; ++i) { .... } } trida.cpp push 2 y = -1 i = 0 loop: if( i >= 10) goto ... ... ++i goto loop push 1 s = call ob.slozitaFce předání parametrů a volání push 1 s = call ob.slozitaFce add esp, 8
Počítání oveček • Spočtěte • počet znaků, řádek, slov, vět • počet a součet čísel
Počítání oveček • Spočtěte • počet znaků, řádek, slov, vět • počet a součet čísel • Upřesnění zadání • zdroj dat: cin, obecný istream • co to je slovo, věta • různá funkčnost vs. neopakovatelný vstup • Postup • funkční návrh • objektový návrh, rozhraní ! • implementace
Kousky kódu jakýkoliv vstupní stream (cin, soubor, řetězec, ...) include <cctype> if( isspace( c)) isalpha, isdigit if( c == '\n') .... include <iostream> fce( istream& s) { char c; for(;;) { c = s.get(); if( s.fail()) return; process( c); } (pokus o) načteníjednohoznaku (nemusí se povést) detekce jakékoliv chyby (např. EOF) platná načtená hodnota jaký je rozdíl? co je lepší? zpracování std vstupu fce( cin); ifstream f; f.open( "file.txt"); if( ! f.good()) .... fce( f); for( i = 0; i < 10; i++) { ... } zpracování souboru for( i = 0; i < 10; ++i) { ... }
Ovečky 1 int pocet_znaku; intpocet_slov; void spocitej( ...) { for(;;) { c = s.get(); .... if( isspace( c)) { pocet_slov = ... if( ...) ... } } void spocitej( ...) { int pocet_znaku; intpocet_slov; for(;;) { c = s.get(); .... if( isspace( c)) { pocet_slov = ... if( ...) ... } }
Ovečky2 class Ovecky { public: void spocitej( ...); private: .... }; void Ovecky::spocitej( ...) { int pocet_znaku; intpocet_slov; for(;;) { c = s.get(); .... if( isspace( c)) { pocet_slov = ... if( ...) ... } }
Ovečky3 class Ovecky{ public: void zpracuj_znak( char c); void spocitej( ...); intpocet_znaku() { return ..; } intpocet_slov() { return ..; } private: int pocet_znaku_; intpocet_slov_; }; void Ovecky::zpracuj_znak( char c) { if( isspace( c)) { pocet_slov_ = ... if( ...) ... } void Ovecky::spocitej( istream& s) { for(;;) { c = s.get(); .... zpracuj_znak( c); } int main() { Oveckyov; ov.spocitej( cin); ... } int main() { Oveckyov; for(;;) { c = s.get(); .... ov.zpracuj_znak( c); mach_etwas( c); } ... }
Ovečky4 ovecky.cpp #include "ovecky.h" void Ovecky::zpracuj_znak( ...) { ... } void Ovecky::spocitej( istream& s) { ... } guard ovecky.h #include <iostream> #ifndef OVECKY_H_ #define OVECKY_H_ class Ovecky{ public: void zpracuj_znak( char c); void spocitej( std::istream& s); intpocet_znaku() { return ..; } intpocet_slov() { return ..; } private: int pocet_znaku_; intpocet_slov_; }; #endif main.cpp #include <iostream> #include "ovecky.h" int main() { Oveckyov; ov.spocitej( cin); cout << ov.pocet(); }
Počítání oveček revisited • Spočtěte • počet znaků, řádek, slov, vět, počet a součet čísel • poslední řádka nemusí být ukončená '\n' • řádky jen ty, kde je (alespoň) slovo nebo číslo • slovo je posloupnostalfanumerických znaků nezačínající číslicí • číslo (posloupnost číslic) nesousedí bezprostředně s písmenem • abc32 je slovo, !32. je číslo, 32abc není nic • každá započítanávěta obsahuje alespoň 1 slovo • '...' ani '31.12.2013' nejsou tři věty • spočítat ze std vstupu nebo ze souborů uvedných na příkazové řádce • objektově a modulárně hezky • všichni poslat! • kdo nestihne na cvičení, dodělat doma
Sekvenční kontejnery • vector- pole prvků s přidáváním zprava • celočíselně indexováno, vždy od 0 • všechnyprvkyumístěny v paměti souvisle za sebou • při přidání možná změna lokace, neplatnost iterátorů! • odvozené: queue, stack • deque[dek] - fronta s přidáváním a odebíráním z obou stran • double-ended queue • prvky nemusí být umístěny v paměti souvisle • lze přidávat i doleva • list - obousměrně vázaný seznam • vždy zachovává umístění prvků • nepodporuje přímou indexaci • forward_list- jednosměrně vázaný seznam • basic_string - posloupnost ukončená terminátorem • string, wstring • array - pole pevné velikosti [dekjú] ≈ dequeue odebrat z fronty C++11 C++11
Asociativní kontejnery • setříděné • setříděné podle operátoru < • pro neprimitivnítypy(třídy) nadefinovat operator< • set<T> - množina • multiset<T> - množina s opakováním • map<K,T> - asociativní pole - parciální zobrazení K -> T • multimap<K,T> - relace s rychlým vyhledáváním podle klíče K • pair<A,B> - pomocná šablona - uspořádané dvojice • položky first, second • šablona funkce make_pair( f,s) • nesetříděné • unordered_set/multiset/map/multimap • hash table - nesetříděné, vyhledávání pouzena == • pro neprimitivnítypy(třídy) nadefinovat hashovací funkci • size_t hash<X>(const X &) C++11
Struktura kontejnerů polootevřený interval
Iterátory • Iterátor • objekt reprezentující odkazy na prvky kontejneru • operátory pro přístup k prvkům • operátory pro procházení kontejneru • kontejner<T>::iteratoriterátor příslušného kontejneru • iterátor je typovaný • kontejner<T>::const_iterator konstantní iterátor - používejte! • *it, it->x přístup k prvku/položce přes iterátor • ++it posun na následující prvek • +(int) -(int)posuniterátoru • begin(), end() iterátor na začátek /za(!) koneckontejneru • rbegin(), rend() reverzní iterátor vector<int> pole= { 10, 11, 20 }; vector<int>::const_iterator i; for( i = pole.begin(); i != pole.end(); ++i) cout << *i;
Základní metodykontejnerů • jednotné rozhraní nezávislé na typu kontejneru • ALE: ne všechny kontejnery podporují vše! • push_back(T), push_front(T)přidání prvku na konec/ začátek • pop_front(), pop_back() odebrání ze začátku / konce • nevrací hodnotu, jen odebírá! • front(), back() prvek nazačátku / konci • operator[], at() přímý přístup k prvku • bezkontroly, s kontrolou(výjimka) • insert (T), (it, T)vložení prvku, před prvek • insert(it,it b,it e) vložení intervalu • insert(make_pair(K,T)) vložení do mapy - klíč, hodnota • erase(it), erase(it,it) smazání prvku, intervalu • find(T) vyhledání prvku • size(), empty()velikost /neprázdost • clear()smazání kontejneru • upper_bound, lower_bound hledání v multisetu/mapě • ... and many many others
Základní příklady #include <vector> map, unordered_map, ... vector<int> pole= { 10, 11, 20 }; pole.push_back( 30); vector<int>::const_iterator i; for( i = pole.begin(); i != pole.end(); ++i) cout << "[" << *i << "]"; map<string,int> m; m.insert( make_pair( "jedna", 1)); map<string,int>mapa; map<string,int>::const_iterator it; fce( map<string,int>& mm); Základní práce s kontejnery Překladový slovník neopisujtestále deklarace ! typedefmap<string,int>Mapka; Mapka::const_iterator it; fce( constMapka& mm); • +slovocizi • slovocizi • slovo • ? slovo-> cizicizicizi • Proč: • neupíšu se • změna druhu nebo typu • rozlišení logicky různých typů • čitelnost
Q & A • Do you need to be able to insert a new element at an arbitrary position in the container? you need a sequence container: associative containers won't do • What category of iterators do you require? If they must be random access iterators... you're limited to vector, deque, and string • Is it important to avoid movement of existing container elements when insertions or erasures take place? you'll need to stay away from contiguous-memory containers
Q & A • Is lookup speed a critical consideration? you'll want to look at hashed containers (unordered_map), sorted vectors, and the standard associative containers — probably in that order • Do you need to minimize iterator, pointer, and reference invalidation? Use node-based containers, because insertions never invalidate any references (unless they point to an element you are erasing). Insertions or erasures on contiguous-memory containers may invalidate all references into the container.
Příkládky vector <string> pole; .... x = pole.size(); pole[x] = 0; vector<int> v; ... vector<int>::const_iterator i; for( i = v.begin(); i != v.end(); ++i) cout << *i << " "; prolezeni pole 5 prvkůdopředu pozpátku úkol: načíst z cin a vypsatodzadupodvou, pakzasezepředu co je zdešpatně? vector<int>::reverse_iterator i; for( i = v.rbegin(); i != v.rend(); ++i) cout << *i << " "; 1 2 3 4 5 6 7 7 5 3 1 2 4 6
Odzadu a zase zepředu • opatrně • pozor na korektnost • mnoheminteligentnější řešení • rovnou při čtení rozhazovat na strany do deque (nebo list) void vypis( vector<int> & v) { vector<int>::const_iterator i; i = v.end(); if( i == v.begin()) return; --i; for(;;) { cout << *i << ", "; if( i == v.begin() || i-1 == v.begin()) break; i -= 2; } if( i == v.begin()) ++i; // vytisteno [0] -> [1] else --i; // vytisteno [1] -> [0] for(;;) { cout << *i << "; "; if( i+1 == v.end() || i+2 == v.end()) break; i += 2; } cout << endl; }
Procházeníkontejnerů vector<int> x; for( inti = 0; i < x.size(); ++i) .. x[i] .. jen pro vector/array int y[MAX]; for( int *i = y; i != y+MAX; ++i) .. *i .. historie:pointrová aritmetika jednotný průchod kontejnery vector<int>::const_iterator i; for( i = x.begin(); i != x.end(); ++i) .. *i .. type inference for( auto i = x.begin(); i != x.end(); ++i) .. *i .. C++11 for( auto i : x) .. i .. range-based for reference naprvek
Setřídit řetězce - vector, list pre, set #include <vector> #include <string> #include <iostream> #include <algorithm> using namespace std; int main() { string s; vector<string> v; for(;;) { cin >> s; if( cin.fail()) break; v.push_back(s); } sort(v.begin(),v.end()); vector<string>::const_iterator i; for( i = v.begin(); i != v.end(); ++i) cout << "[" << *i << "] "; cout << endl; } list<string> v; list<string>::const_iterator i; for(;;) { cin >> s; if( cin.fail()) break; for( i = v.begin(); i != v.end() && *i <= s; ++i) ; v.insert( i, s); } string s; set<string> v; for(;;) { cin >> s; if( cin.fail()) break; v.insert(s); } jak to setřídit? vlastní třídění • bool mysort( const string& s1, const string& s2) { • return s1.size() < s2.size() ? true : • (s1.size() > s1.size() ? false : s1 < s2) • } • set<string, mysort> v; • ... • v.insert(s);
Nejpoužívanější algoritmy #include <algorithm> • it find( it first, it last, T&) • int count( it first, it last, T&) • for_each(it first, it last, fnc( T&)) • transform(it first, it last, output_it out, fnc( T&)) • copy(it first, it last, output_it out) • sort( begin, end, sort_fnc(x&, y&)) • find_if, count_if, remove_if( it first, it last, pred& p) funkce modifikujeargument vrací modifikovaný argument možnost jiného kontejneru predikát: bool fnc( const T&)
Algoritmy - použití #include <algorithm> vector<int> v = { 1, 3, 5, 7, 9 }; // vector<int>::const_iterator result; auto result = find( v.begin(), v.end(), 5); predikát boolgreater10 ( int value ) { return value >10; } result = find_if( v.begin( ), v.end( ), &greater10 ); if ( result == v.end( ) ) cout << "Nothing" << endl; else cout << "Found: " << *result << endl; vždy otestovat! for_each( begin, end, fnc( T&)) // vynásobit všechny prvky 2 // přičíst ke všem prvkům +1, +2, +3, ... void mul2( int& x) { x *= 2; }
Algoritmy - použití jak parametricky? #include <algorithm> vector<int> v = { 1, 3, 5, 7, 9 }; // vector<int>::const_iterator result; auto result = find( v.begin(), v.end(), 5); boolgreater10 ( int value ) { return value >10; } result = find_if( v.begin( ), v.end( ), &greater10 ); if ( result == v.end( ) ) cout << "Nothing" << endl; else cout << "Found: " << *result << endl; for_each( begin, end, fnc( T&)) // vynásobit všechny prvky 2 // přičíst ke všem prvkům +1, +2, +3, ... int fce( int& x) { static int qq = 0; return x += (qq +=1); } for_each( v.begin(), v.end(), fce); for_each( v.rbegin(), v.rend(), fce); jakzrestartovat? jakkrokparametricky?
Funktory class ftor { public: ftor( int step) : step_(step), qq_(0) {} int operator() (int& x) { return x += (qq_ += step_); } private: int step_; int qq_; }; for_each( v.begin(), v.end(), ftor(2)); Přičíst ke všem prvkům +n, +2n, +3n, ... Funktor – třída spřetíženým operátorem () oddělení inicializace a běhového parametru it = find_if( bi, ei, fnc); class cmp { public: cmp( int cmp) : n_(cmp) {} bool operator() (int& x) { return x > n_; } private: int n_; }; auto fnd = find_if( v.begin(), v.end(), cmp(9)); cout << ( ( fnd == v.end()) ? -1 : *fnd) << endl; najít v kontejneru prvek větší než n • najít prvek odlišný od předchozího alespoň o n • inkrementovat čísla v zadaném rozsahu hodnot(první +1, druhé +2, ...) • najít číslo za největší dírou
Návratováhodnotafor_each součet všech číselvětších než parametr class scitacka { public: scitacka( int limit) : limit_(limit), vysledek_(0) {} int operator() (int& x) { if( x > limit_) vysledek += x; } int vysledek_; private: int limit_; }; scitacka s = for_each( v.begin(), v.end(), scitacka(10)); cout << s.vysledek_ << endl; jak získat výsledek? po skončení hodnota použitého funktoru pozor! nejde o identický objekt
Lambda výrazy • najděte všechny prvky větší než 9 • lambda výrazy • od C++ 11 • mnohem jednodušší zápis a syntaxe • vnitřní stav funktor find_if( v.begin(), v.end(), bind2nd( greater<int>(), 9)); find_if( v.begin(), v.end(), greater_than(9)); find_if( v.begin(), v.end(), [](int& x) { return x > 9; }); vlastní funktor lambda výraz
Nonmodyfying algorithms for_each() Performs an operation for each element count() Returns the number of elements count_if() Returns the number of elements that match a criterion min_element() Returns the element with the smallest value max_element() Returns the element with the largest value find() Searches for the first element with the passed value find_if() Searches for the first element that matches a criterion search_n() Searches for the first n consecutive elements search() Searches for the first occurrence of a subrange find_end() Searches for the last occurrence of a subrange find_first_of() Searches the first of several possible elements adjacent_find() Searches for two adjacent elements that are equal equal() Returns whether two ranges are equal mismatch() Returns the first elements of two sequences that differ lexicographical_compare() Returns whether a range is lexicogr. less
Modifying algorithms for_each() Performs an operation for each element copy() Copies a range starting with the first element copy_backward() Copies a range starting with the last element transform() Modifies and copies elements; combines two ranges merge() Merges two ranges swap_ranges() Swaps elements of two ranges fill() Replaces each element with a given value fill_n() Replaces n elements with a given value generate() Replaces each element with the result of an operation generate_n() Replaces n elements with the result of an operation replace() Replaces elements that have a special value replace_if() Replaces elements that match a criterion replace_copy() Replaces elements that have a special value while copying the whole range replace_copy_if() Replaces elements that match a criterion while copying the whole range
Removing algorithms remove() Removes elements with a given value remove_if() Removes elements that match a given criterion - does not remove anything! remove_copy() Copies elements that do not match a value remove_copy_if() Copies elements that do not match a given criterion unique() Removes adjacent duplicates unique_copy() Copies elements while removing adjacent duplicates
Mutating algorithms reverse() Reverses the order of the elements reverse_copy() Copies the elements while reversing rotate() Rotates the order of the elements rotate_copy() Copies the elements while rotating their order next_permutation() Permutes the order of the elements prev_permutation() Permutes the order of the elements random_shuffle() Brings the elements into a random order partition() Changes the order of the elements so that elementsthat match a criterion are at the front stable_partition() Same as partition(), but preserves the relative order of matching and nonmatching elements
Sorting algorithms sort() Sorts all elements stable_sort() Sorts while preserving order of equal elements partial_sort() Sorts until the first n elements are correct partial_sort_copy() Copies elements in sorted order nth_element() Sorts according to the nth position partition() Changes the order of the elements so that elements that match a criterion are at the front stable_partition() Same as partition(), but preserves the relative orderof matching and nonmatching elements make_heap() Converts a range into a heap push_heap() Adds an element to a heap pop_heap() Removes an element from a heap sort_heap() Sorts the heap (it is no longer a heap after the call)
Algorithms for Sorted Ranges binary_search() Returns whether the range contains an element includes() Returns whether each element of a range is alsoan element of another range lower_bound() Finds the first element greater than or equal toa value upper_bound() Finds the first element greater than a given value equal_range() Returns the range of elements equal to a given value merge() Merges the elements of two ranges set_union() Processes the sorted union of two ranges set_intersection() Processes the sorted intersection of two ranges set_difference() Processes a sorted range that contains all elementsof a range that are not part of another set_symmetric_difference() Processes a sorted range that contains all elementsthat are in exactly one of two ranges inplace_merge() Merges two consecutive sorted ranges
Numeric algorithms accumulate() Combines all element values (processes sum, product, ...) inner_product() Combines all elements of two ranges adjacent_difference() Combines each element with its predecessor; converts absolute values to relative values partial_sum() Combines each element with all of its predecessors; converts relative values to absolute values
Příklady na algoritmy • vidle • vstup: vektor čísel • výstup: multiset čísel větších než X inkrementovaný o Y • filmovádatabáze • název filmu, režisér, rok, ... • vypsat setříděné • podle roku a názvu filmu • podle režiséra a roku • jednoduchý makroprocesor • vstup: text #novemakroobsahmakra#dalsinovemakrokonec • vystup: text dalsiobsahmakrakonec • vstup: tt #m1 xx # #m2 yym1 # pozor - v definicimakra může být obsažena hodnota jiného makra
Polymorfní datové struktury Zadání: • kontejner obsahující čísla libovolného typu • int, double, string, complex, ... Technické upřesnění: • třída Seznam • operace append, print • společný předek prvků AbstractNum • konkrétní prvky IntNum, DoubleNum, ... • stačí jednoduchá implementace polem • pole objektů vs. pole odkazů S IN DN IN AN AN AN x d x
Polymorfní datové struktury - kostra tříd class AbstractNum { public: virtual void print()=0; virtual ~AbstractNum() {} }; class Seznam { public: void append(AbstractNum *p); void print(); Seznam(); ~Seznam(); private: enum {MAX=100}; AbstractNum* pole[MAX]; int n; }; abstraktní předek umí existovat a vytisknout se virtuální destruktor! int main(int argc, char** argv){ Seznam s; s.append( new .... ); s.append( new .... ); s.append( new .... ); s.print(); return 0; } přidávání dynamicky vytvořených konkrétních typů