Poznámka: Příklady jsou zjednodušené a ne vždy jsou 100% syntakticky právně (nechci vždy psát int main(void)... etc.) # Principy objektového programování 1) Jaký je rozdíl mezi třídou a objektem? **Třída je šablona pro vytváření objektů**. Typicky napíšu třídu a pak z ní inicializuju její objekty, které až poté v kódu využívám. ```cpp class Trida { public: int cislo; // tady jsou funkce a promenne }; int main(void) { Trida objekt; objekt.cislo = 10; } ``` 2) Jak je určena viditelnost členských proměnných a metod? - public -> přístup možný odkudkoli z programu - protected -> přístup je možný ze třidy nebo z podtříd (dědičnost) - private -> pouze funkce uvnitř objektu (třídy) můžou přistupovat ke členu s touto viditelností ```cpp class Trida { public: int a; private: int b; protected: int c; }; int main(void) { } ``` 3) Jaký je rozdíl mezi členskými metodami (a proměnnými) třídy a objektu? Třída může obsahovat i metody typu `static` to jsou **členské metody**. Pokud ze třidy inicializuju objekt, tyto metody se do objektu nepředají. Nejlepší způsob jak je zavolat je přes jejich třídu `Mojetrida::metoda();` 4) Jaký je rozdíl mezi klíčovými slovy protected a private? - **private** -> přístupné **POUZE** uvnitř třídy, zděděná třída (a její objekty) nemá přístup k k proměnné původní třídy s protected - **protected** -> vlastně je to jako private (tedy z kódu nemůžu měnit tuto proměnnou/metodu ve třidě), ale s tím rozdílem, že pokud vytvořím podtřídu (pomocí dědění) a z ní vytvořím objekt, tak jeho metody můžou přistupovat k protected členům objektů původní třídy - Co je polymorfismus a jak je ho dosaženo? - Co je to bázová třída a čím se vyznačuje? - Uveďte příklad deklarace plně virtuální metody? - Co je to implicitní konstuktor? - Co je to kopírovací konstruktor? - Jaké jsou možnosti dědění? Uveďte příklady. - Co je to virtuální dědění a k čemu je dobré? - Které vlastnosti rodiče potomek nedědí? - Co je to přetížení? Uveďte příklad (několik řádek kódu, ze kterých bude zřejmé, o co jde). - Jaký je rozdíl mezi přepsáním (override) a přetížením (overload)? - Co je vazba typu kompozice? Uveďte příklad (několik řádek kódu, ze kterých bude zřejmé, o co jde). - Co je vazba typu asociace? Uveďte přiklad (několik řádek kódu, ze kterých bude zřejmé, o co jde). - Co reprezentuje klíčové slovo this? - Jaký je rozdíl mezi referencí a ukazatelem? - Jaký je rozdíl mezi mělkou a hlubokou kopií? # Programování v C++ ### 1) Napište příklad načteni hodnoty typu int ze standarního vstupu pomocí proudového operátoru. Jak lze ošetřit správnost vstupu? ```cpp #include int main(void) { int x; if(std::cin >> x) { std::cout << "ok"; } else { std::cout << "chyba"; } } ``` Tenhle příklad načte hodnotu pomocí `std::cin >> x`, kde `x` je proměnná a `std::cin` je standardní input stream. `std::cin >> x` vrací hodnotu `true` pokud je vše v pořádku a `false` pokud ne. Pokud se načte něco jiného jak číslo, tak třeba vrací false. Kokrétněji `std::cin` má několik stavových bitů a to **fail** -> který je nastavený, když se něco pokazí (například špatný převod na typ -> string na int, etc.), **eof** -> který je nastavený, když je konec input streamu, **bad** -> který je nastavený, když se opravdu něco pokazí. Když přečtu poslední znak, automaticky se nastaví eof bit a ten můžu zkontrolova pomocí `std::cin.eof() == true`. Složitější je, když se něco pokazí. To může být např. při špatném převodu nebo, když čtu po konci streamu a **je třeba to opravit**. Nejprve je třeba nastavit `std::cin.clear()`, což vynuluje bity, takže můžu pokračovat ve čtení. To ale nestačí. Nezpracovaný text zůstane uložený v bufferu a přečte se znova příště. To jde vyřešit pomocí funkce `std::cin.ignore(int, char)`, kde int je počet znaků, co to vymaže a char je znak, kterým to končí, například `std::cin.ignore(1000, '\n')` vymaže 1000 znaků do konce řádky. Btw `if(std::cin >> x)` vlastně vrací `if(!std::cin.fail())` ### 2) Jak se v C++ provádí přetížení operátoru? Uveďte příklad. Přetěžování operátorů je implementace fungování znaků operátorů jako `+`, `-`, `>`, etc. pro objekty tříd, co napíšu. Jsou dva způsoby jak to udělat. Buď na to napíšu funkci uvnitř třídy, pro kterou to chci implementovat, nebo na to napíšu samostatnou funkci mimo. Používám na to speciální název funkce `operator`, ke které napíšu, jaký operátor řeším Příklad uvnitř třídy: ```cpp class Number { public: int n; Number(int set_n) {n = set_n;} Number operator+(const Number &numA) //reference na objekt napravo od + { return Number(this->n + numA.n) } }; int main(void) { Number a(10); Number b(20); Number c = a + b; } ``` Nebo mimo třídu to může vypadat například takto: ```cpp Number operator+(const Vec& a, const Vec& b) { return Vec{a.x + b.x}; } ``` Btw, reference se používají, protože nekopírovat hodnoty, ale jenom na ně ukazovat, je šetrnější. **Na tohle téma jsem udělal samostatný dokument na githubu (Přetěžování operátorů)** ### 3) Jaké operátory nelze v C++ přetížit? Přetěžovat nelze operátory: - `.` - `::` - `?:` - `sizeof` - `typeid` Většinou to je proto, protože jsou v syntaxi tak důležité, že by to mohlo udělat nejasnosti v tom, jak by to měl vůbec kompilátor překládat. (Má tečku řešit jako člen, nebo jako něco úplně jiného... etc.) **Ty operátory totiž už většinou mají funkční účel předtím, než jsou přetížené a to u operátorů, co jdou přetížit neplatí.** Na tohle téma jsem zpracoval mimochodem dokument na githubu. ### 4) Jak a kdy se volá konstruktor a destruktor? Konstruktor se zavolá při vytvoření objektu. Můžu do něj dát jako argument hodnoty, které chci třeba využít při inicializaci. Mám dva typa alokace objektů, které se dělají jinak. Jedna je statická a druhá je dynamická. Staticky alokuju objektu takto: `Trida objekt(...);` a dynamicky pomocí **new**: `Trida* objekt = new Trida;`. Je v tom zásadní rozdíl v životnosti objektu. -> viz další otázka: Destruktor se u statického objektu zavolá sám, když se dojde ke konci scopu (bloku kódu, funkce, etc...). Nemusím ho volat a ani bych neměl. U dynamického objektu ho musím zavolat manuálně pomocí `delete objekt;` nebo v případě pole objektů pomocí `delete[] pole_objektu;` Pozn: klasický dynamický objekt se řeší pomocí ukazatele na objekt a dereference ### 5) Jaký je rozdíl mezi objektem alokovaným v zásobníku a objektem dynamicky alokovaným na haldě? - Statické objekty se automaticky zníčí, když opustí scope -> blok, funkci, etc. - Dynamické objekty zůstávají v paměti, než jsou zničeny pomocí delete Používají se často alternativy, například smart pointer -> drží pointer v objektu a když se zníčí, zníčí i dynamický objekt. ### 6) Jaký je rozdíl mezi operátory `delete` a `delete[]`? - `delete` se používá pro uvolňování dynamického objektu - `delete[]` se používá pro uvolňování pole objektů ```cpp Trida* objekty = new Trida[7]; delete[] objekty; //uvolni cele pole objektu ``` ### 7) Jaká je návratová hodnota destruktoru? Destruktor nemá návratovou hodnotu. Jako u funkce void se z něj dá vrátit pomocí `return;` ### 8) Co je třeba zajistit při dynamické alokaci? Uveďte příklad. Při dynamické alokaci si hlavně musím dávat pozor na to, že mám nad pamětí nonstop kontrolu (například nikde neztratím ukazatel na ní) a že ji správně dealokuju (nedojde k úniku paměti). V moderním C++ to ale tolik řešit nemusím. Většinou použiju tzv. **smart pointer**. který automaticky dealokuje objekt, když skončí scope. ```cpp #include //tohle je na smart ukazatele #include struct Node {int value;}; void main(void){ auto p = std::make_unique(); // alokace } //po konci main automatické uvolnění ``` ### 9) Co je to výjimka a k čemu je dobrá? Uveďte příklad. Výjimky umožňuje kontrolovat chod programu a popasovat se s nečekanými chybami. Primárně jsou míněny pro řešení problémů, které programátor nemůže tak dobře ovlivnit, jako například práci se zdroji (třeba soubor) nebo množství dynamické paměti. Výjimky existují všeho různého druhu, zde je příklad kódu: ```cpp try { #nějaký kod } //catch("Text") {} //nedává smysl, nechytá se hodnota, ale typ catch(const std::bad_alloc& e) {} //vyjímka špatné alokace catch(int) {} //chytí všechny integery - které manuálně vyhodím catch(std::exception) {} //chytí všechny chyby (věci typu chyba) catch(...) {} //chytí úplně všechno - i to co manuálně vyhodím ``` **Mám na tohle téma samostatný dokument, kde toto téma zpracovávám.** ### 10) Kolik výjimek je možné v programu ošetřit? Tbh, nechápu tuhle otázku - Typů výjimek je hodně a kdyžtak si můžu vlastní dopsat. - výjimek může program za běhu ošetřit neomezeně - **Najednou (v jeden moment) je možné ošetřit pouze jednu výjimku. Pokud se jich objeví více (například z destruktorů), tak se pogram ukončí přes std::terminate** ### 11) Co je to generická funkce? Generická funkce je funkce, která umí přijímat či vracet libovolný datový typ. je to šikovná alternativa k přetěžování funkcí. K tomu používám `template `, kde `T` reprezentuje libovoloný datový typ (to je moje pojmenování, ale jmenovat se to může jak chce, T se používá často...). Příklad i s funkcí: ```cpp template T secti(T a, T b) { return a + b; } ``` Zde sčítám nějaké 2 obecné datové typy. ### 12) Jak lze vytvořit explicitní instanci generické funkce? K čemu je to dobré? - DOCELA MIZERNĚ [stackoverflow](https://stackoverflow.com/questions/2351148/explicit-template-instantiation-when-is-it-used#2351622) Explicitní instance je způsob, jak předem donutit kompilátor vytvořit šablonu - instanci generické funkce, než je použita. ```cpp template int secti(int, int); ``` Ono to údajně umožní zrychlit kompilaci protože tomu řeknu, ať to vytvoří tu int verzi předem. Normálně bych do `.h` souboru napsal: ``` template T secti(T a, T b); ``` ale můžu místo toho do `cpp` souboru napsat: ```cpp template T secti(T a, T b) { return a + b; } template int secti(int, int); // explicitní instance ``` kde se vytvoří verze s intem samostatně předem a pak místo toho, aby to kompilátor řešil 10x zrovna, když to potřebuje, tak to prostě zkopíruje. Also to umožní fungování je některým typům? Takže: - umožní použití jen několik typů, které si určím - zrychlý to kompilaci ### 13) Jak jsou obvykle parametrizovány šablony tříd? ### 14) V čem spočívá problém vícenásobného dědění? Jak je možné tomuto problému zabránit? ### 15) Co je to zapouzdření a čemu je dobré? ### 16) Jak lze využít implicitní hodnoty argumentu funkce? Uveďte příklad. ### 17) Jaký je rozdíl mezi deklarací třídy pomocí class a struct? ### 18) Co je to konverzní konstruktor? ### 19) Jaký je rozdíl mezi statickou a dynamickou vazbou? ### 20) Jaký je význam klíčového slova auto? Lze ho použít vždy? ### 21) Co přesouvací (move) kontruktor? ### 22) Co je pravidlo tří (rule of three)? ### 23) Jaký je tvar definice lambda funkce v C++? Uveďte příklad a popište, co funkce dělá. # STL kontejnery ### 1) Co je to kontejner? Kontejnery jsou datové struktury určené k ukládání kolekce dat (hodnot) a k manipulaci s nimi. Například to je `vector`, který funguje jako dynamické pole. Máme různé typy kontejnerů - sekvenční asociativní (a pak údajně neseřazený asociativní a adaptery pro jiné ovládání existujících kontejnerů, ale to raději neřešme). Viz. další otázka. Hlavní smysl kontejnerů je ulehčit práci při programovaní. Je to často již implementované řešení, které se v kódu jednoduše používá, nebo je to rada, jak si takové řešení udělat. Je to vrstva abstrakce. ### 2) Jaký je rozdíl mezi sekvenčními a asociativními kontejnery? Uveďte příklad. **Sekvenční kontejnery** pracují s daty nezávisle na jejich hodnotě. To je míněno tak, že třeba data ukládají dle pořadí, ve kterém do kontejneru přišli a data mají unikátní index. Je to například `std::vector` a `std::list` **Asociativní kontejnery** řeší data většinou dle nějakého klíče, který je k nim přiřazen. Například `std::map`. Data jsou pak seřazena tak, aby bylo například efektivní vyhledávání a místo indexu mají právě na vyhledání klíč. - `std::map` - data seřazeny dle porovnávací funkce - `std::unordered_map` - data neseřazeny a hledány dle jejich hashe ### 3) Jaké jsou klíčové vlastnosti kontejneru std::vector? **Vector** je prakticky **dynamické** **pole** s podobnými vlastnostmi jako jeho implementace v C++, akorát to celé někdo implementoval v třídě s funkcemi za nás. - Data jsou seřazeny za sebou dle jejich velikosti v paměti. (tedy 5 prvek int o velikosty 4 byty je na 17-20 bytu) - Vyhledávání konkrétního prvku je O(1) -> dopočítám hned z velikosti a pozice - přidání/odstranění na konec - O(1) - Odstranění a přidání je kamkoli - O(n) -> vyžaduje přesun všech prvků v poli - pole má hodnotu velikosti (kolik zabírá bytů prvky) a kapacity (kolik bytů si dynamicky rezervuje). Kapacita musí být větší než velikost, jinak je třeba přealokovat - adresy prvků nejsou stabilní kvůli realokaci `std::vector kontejner1`; ### 4) Jaké jsou klíčové vlastnosti kontejneru std::map? Ukládá si pro každý prvek 2 hodnoty a to **klíč a hodnotu**. Všechny prvky jsou seřazené podle klíče a jeho porovnávací funkce, který zajišťuje operace o O(log n). Seřazení může třeba být binární strom. Kontejner umožňuje vymyslet a vložit vlastní porovnávací funkci v šabloně. ### 5) Jaké jsou klíčové vlastnosti kontejneru std::list? List je kontejner, který vlastně není moc odlišný od toho, co by člověk čekl (pokud si to pamatuje). Data jsou uložena každá ve vlastní buňce v paměti. Každá buňka umožňuje přístup k dalšímu a předchozímu prvku (pamatuji se jejich ukazatele). - proto je každá buňka bezpečně samostatně uložena v paměti nezávisle na ostatních buňkách - Je jednoduché přidávat/odebírat na známé pozice prvky - O(1) -> stačí změnit ukazatele v buňkách předchozí a další buňky (říká se tomu často uzel). - Hledání prvku na n-té pozice je obtížné, je třeba projít všechny prvky, které k němu vedou - O(n) - vhodné pro potřebu čistého rychléhi přidávání a mazaání prvků, ale nevhodné pro rychlý náhodný přístup `std::list L;` ### 6) Jakého datového typu může být klíč kontejneru std::map? Klíč `std::map` může být kteréhokoli typu, který je možná porovnávat pomocí řazení (má operátor `>` a `<`). Nebo je možné dodat vlastní porovnávací funkci (komparátor). ### 7) Co je třeba zajistit v případě, že klíčem v kontejneru std::map je strukturovaný datový typ (např. naše vlastní třída)? - buď je třeba zajistit, aby objekty třídy byly porovnatelné pomocí `>` a `<` například pomocí přetěžování operátorů - nebo je možné sepsat vlastní comparator (vlastní porovnávací funkci) ### 8) Jaký je význam datového typu std::pair? `std::pair` je datový typ, který umí ukládat 2 prvky libovolného typu ```cpp pair p = {1, "ahoj"}; p.first = 7; p.second = "krkavců"; ``` vhodné je se i podívat na operaci vzájemného porovnávání párů ### 9) Co je to iterátor? Uveďte příklad. Iterátor je třída, která je spojená s určitým kontejnerem. Úkol prvků této třídy je jednoduchý a to umožnění jednoduché procházení prvky kontejneru. Iterátor by tedy měl být dereferencovatelný -> dereferencí `*it` získáme hodnotu nynějšího prvku, jako by to byl ukazatel. Dále přičtením `++it` získáme další prvek kontejneru. Iterátoru přiřadíme hodnotu pomocí funkcá `.begin()` -> hodnota prvního iterátoru, a zjišťujeme konec kontejneru pomocí `.end()`. Příklad: ```cpp std::vector pole = {1, 2, 3}; std::vector::iterator it = pole.begin(); for(std::vector::iterator it = pole.begin(); it != pole.end(); ++i) { std::cout << *it << std::endl; } ``` ### 10) Jaký je rozdíl mezi konstantním a nekonstantním iterátorem? - Pomocí **nekontantního** iterátoru můžu měnit hodnotu prvku, na který ukazuje. - Hodnotu prvku na který ukazuje **konstantní** iterátor měnit nemůžu. - Potřebuji změnit typ iterátoru a také používám speciální funkci ```cpp std::vector pole = {1, 2, 3}; std::vector::iterator it = pole.begin(); std::vector::const_iterator cit = pole.cbegin(); //vlastni funkce *it = 10; //můžu *cit = 20; //nesmim, je to const... ``` ### 11) Co všechno je třeba zajistit pro vytvoření vlastního iterátoru? - iterátor musí mít dereferenci -> lze získat hodnotu jeho prvku - lze přičítat takto `++it` - lze odčítat takto `--it' - lze porovnávat `==` a `!=` -> hlavně pro `.begin()` a `.end()` zbytek podle kontejneru a kontextu (a podle toho, jak je vlastně vůbec myšlena tato otázka) - je třeba mít `end` a `begin`, konstruktor u kontejneru etc... ### 12) Co je range-for cyklus? Uveďte příklad. Je to specifický zápis `for` loop, který umžňuje iterovat všemi prvkami kontejneru. ```cpp std::vector pole = {1, 2, 3}; for (auto &i : pole) { i++; } ``` V tomto kódu se projedou všehny hodnoty vektoru a přičte se k ním jedna. `for (auto &i : pole)` -> napravo je název pole, nalevo je hodnotu prvku, používá se auto s referencí, aby se fyzicky nekopíroval každý prvek a bylo to nezávislé na typu. prvek `i` pak reprezentuje aktuálně prvek iterace, tedy nabere hodnotu každého prvku pole **Obdobně lze udělat loop i pro iteraci listem**. ### 13) Uveďte příklad použití algoritmu sort (deklarujte vektor libovolného datového typu, vložte do něj data a vektor seřaďte). ```cpp #include #include #include int main() { std::vector v = {5, 2, 9, 1, 7}; std::sort(v.begin(), v.end()); for (const auto& x : v) { std::cout << x << " "; } return 0; } ``` Sort porovná prvky podle jejich operárů `<` a `>`, funguje univerzálně u všech typů, pokud mají porovnávání definované. U stringů to třeba porovnává lexikograficky. Jinak je třeba definovat `>` `<` pomocí přetěžování operátorů a nebo dodat vlastní porovnávací funkci. -> `std::sort(v.begin(), v.end(), comparator)` -> kde comparator je porovnávací funkce # Praktické programování (Qt a související) - Co je to event-driven programování? - Jaký je rozdíl mezi událostí a signálem? - Uveďtě příklady událostí v Qt a jejich využití. - Popište systém signálů a slotů, uveďte příklad propojení. - Jakým způsobem lze vytvořit v Qt okno a v něm vykreslit grafickou komponentu? - Jak lze měnit grafickou podobu jednotlivých Qt komponent? - Co se časovač (QTimer), k čemu je dobrý a jak se používá? - Co si představujete pod pojmem MVC? - Jaký je rozdíl mezi UDP a TCP transportní vrstvou? - Co je to REST rozhraní?