From 7440ae1b9a7d90066155e783a9d92be6b182f40c Mon Sep 17 00:00:00 2001 From: Lugaricci Date: Mon, 6 Apr 2026 18:21:35 +0200 Subject: [PATCH] dodelane okruhy krom Qt, ale chtelo by to jeste trochu lasky --- Zpracované Otázky.md | 217 ++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 215 insertions(+), 2 deletions(-) diff --git a/Zpracované Otázky.md b/Zpracované Otázky.md index 68c4a2e..a75de3e 100644 --- a/Zpracované Otázky.md +++ b/Zpracované Otázky.md @@ -106,6 +106,8 @@ public: //vracím objekt s novým ukazatelem u a } ``` + +Fungování má podobné jako normální konstruktor. Místo toho, aby vytvořil nový prvek z dodaných argumentů, vezme referenci na jiný objekt jako argument. ### Jaké jsou možnosti dědění? Uveďte příklady. Celé je to o tom, jak se změní viditelnost členů. Máme **3 typy dědění**: @@ -465,16 +467,227 @@ 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? +Obvykle to je `template `, ale `template ` je ekvivalentní. +Můžu taky parametrizovat třeba konstantní hodnotou, např. `tepmlate ` nebo více parametrů `template ` + ### 14) V čem spočívá problém vícenásobného dědění? Jak je možné tomuto problému zabránit? + +Největší problém je tzv. "diamond problem", který už jsem řešil v jedné otázce v OOP. Jde o to, že při dědění po druhé generaci může u vícenásobném dědění (zdědění ze dvou tříd najednou) dojít k dvojitéme zdědění členů z původní bázové třídy (v tomto případě "dědovi"). Zabránit tomu jde pomocí tzv. virtuálního dědění. (viz otázkac v OOP) ### 15) Co je to zapouzdření a čemu je dobré? +Zapouzdření je koncept ochrany vnitřního stavu objektu. Nemusí být žádoucí, aby externí funkce/objekty/kód měli přímí přístup ke členům objektu, proto se implementuje vrstva jejich ochrany. +Dobrý příklad jsou například **gettery** a **settery**. Místo toho, aby se přímo přistupovalo k vnitřní proměnné, tak je vnitřní proměnná `private` a implementuje se sunkce, která je přístupné a umí proměnnou změnit (tzv. **setter**) nebo funkce, která tu proměnnou umí přečíst (tzv. **getter**) + +Je to jeden ze základních principů OOP. ### 16) Jak lze využít implicitní hodnoty argumentu funkce? Uveďte příklad. +Implicitní hodnota argumentu funkce mi umožňuje při volání nezadávat všechny argumenty a **místo toho použít nějakou předvyplněnou hodnotu**, které se použije, když hodnotu specificky neurčím. Např: + +```cpp +int soucet(int a, int b = 0) {return a + b;} + +//... + +soucet(5, 3); // vraci 8 +soucet(5); // vraci 5 (b = 0) + +int f(int a = 1, int b); // chyba +``` + +Je potřeba psát implicitní hodnoty **zprava doleva**. Když vyplňuji hodnoty, tak se totiž vyplňují zleva. Jinak to nebude fungovat, nebylo by jasné, jaké mám být chování vyplňování. ### 17) Jaký je rozdíl mezi deklarací třídy pomocí class a struct? -### 18) Co je to konverzní konstruktor? +[geeks for geeks](https://www.geeksforgeeks.org/cpp/structure-vs-class-in-cpp/) +V podstatě jsou velmi podobné. Hlavní a největší je automatická přístupnost členů (která se nastaví bez explicitního udání). U class je to **private** a struct je to **public** + +```cpp +struct A +{ + int x; //je public +}; + +class B +{ + int x; //je private +} +``` +### 18) Co je to konverzní konstruktor? RADĚJI SE NA TO JEŠTĚ JUKNOUT +Konverzní konstruktor je vlastně normální konstruktor s jedním parametrem, který umožňuje převedení nějakého typu na třídu. + +```cpp +class A { +public: + int a + A(int x) {this->a = x;} +}; + +void f(A obj) {} + +f(10); //tady proběhne konverze +``` + +zajímavé je použití s modifikátorem `explicit`, který zabrání zjednodušení typu. +```cpp +//udajne smim toto +A a(5.7); +//a i toto +A a = 5.6; +``` + +Pokud ale: +```cpp +class A { +public: + int a + explicit A(int x) {this->a = x;} //explicit +}; + +void f(A obj) {} + +f(10); //tady proběhne konverze +``` + +Tak +```cpp +//smim toto +A a(5.7); //zmeni na 5 +//a nesmim toto +A a = 5.7; +``` ### 19) Jaký je rozdíl mezi statickou a dynamickou vazbou? +Vazba je zpsůsob přiřazení funkci ke svému bloku a zde mluvíme zpravidla o **třídach**. + +**Statická vazby:** +Když mám normální obyčejnou funkci ve třídě např: +```cpp +class Zvire +{ +public: + void zvuk() {cout << "Zvuk zvírete\n";} +}; + +class Pes : public Zvire +{ +public: + void zvuk() { out << "Haf!\n";} +}; + +int main() +{ + Zvire z = Pes(); // Objekt Pes je "převlečen" na Zvire + z.zvuk(); // Zavolá Zvire::zvuk() - "Zvuk zvírete" + return 0; +} + +``` + +Tak rozhodnutí o přiřazení fuknce ke třídě (a jejím objektům) proběhne při kompilaci. Při běhu programu je to rychlé a efektivní, ale je to pevně dané a neflexibilní. **Volaná funkce se rozhodne dle typu objektu a ne podle opravdového typu instance.** + +**Dynamická vazby:** +Pokud chci vyřešit tento problém, můžu donutit kompilátor, aby zařídil, že se program aktivně za běhu pamatuje, jakého typu jsou objekty a na jaké funkce odkazují. To je ale pomalejší a paměťově náročnější. Zařizuji to pomocí `virutal` před funkcí u bázové třídy: + +```cpp +class Zvire +{ +public: + virtual void zvuk() { cout << "Zvuk zvírete\n"; } + virtual ~Zvire() {} +}; + +class Pes : public Zvire +{ +public: + void zvuk() override { cout << "Haf!\n"; } +}; + +int main() +{ + Zvire* z = new Pes(); // Ukazatel na Zvire, ale skutečný objekt je Pes + z->zvuk(); // Zavolá Pes::zvuk() - "Haf!" + delete z; + return 0; +} + +``` + +Technicky je to zařízené pomocí tzv. **vtable** neboli tabulky virtuálních funkcí. ### 20) Jaký je význam klíčového slova auto? Lze ho použít vždy? +`auto` se používá jako náhrada datové typu. Kompilátor sám pozná a automaticy použije datový typ, který uzná za vhodný. To nám může ulehčit práci v různých případech, například u iterátorů. + +`auto` ale nelze použít vždy, problém je třeba používání **u definování argumentů funkce** nebo ### 21) Co přesouvací (move) kontruktor? +Přesouvaní konstruktor slouží k převzetí zdrojů jiného objektu. V podstatě přijme hodnoty původního objektu (mělce včetně ukazatelů) a pak vymaže původní objekt (nebo ho vyprázdní). + +Např: +```cpp +class Trida { +public: + int* data; + + A(int x) {data = new int(x);} //konstruktor + + // move konstruktor + A(A&& other) { //rvalue reference -> bere pravou stranu x = y -> tedy y + data = other.data; // převezme ukazatel + other.data = nullptr; // zabrání double delete + } + + ~A() { + delete data; + } +}; +``` + +Pak jde například: +```cpp +Trida a1 = Trida(20); //prirazuji rvalue hodnotu do a1 pres move consturtor +Trida a2(30); //delam pomocny objekt +Trida a3 = Std::move(a2); //prirazuji pomocny a2 pres move constructor +//Std::move() zmeni svuj obsah na rvalue, takze se zavola move consturctor a ne kopirovaci konstructor +``` + +V podstatě jde o to, že konstruktory se volají dle toho, jestli je přiřazována **rvalue** neb **lvalue**. Lvalue je standardní hodnota jako třeba jiný objekt. Rvalue jsou dočasné objekty, které brzo přestanou existovat. Funkce Std::move() změní svoji hodnotu na rvalue, pak by se něměla ideálně takové změněná hodnotu už používat. + +Ale tady jsi raději najděte o čem mluvím, sám tomu moc nerozumím. ### 22) Co je pravidlo tří (rule of three)? +[Rule of three](https://en.wikipedia.org/wiki/Rule_of_three_(C%2B%2B_programming)#Rule_of_five) (nebo alternativně *rule of five*) je pravidlo, které se zabývá tím, jak správně vytvořit třídu a hlavně tedy, co všechno má obsahovat. + +**Rule of three: třída má obsahovat** +- destruktor +- kopírovací konstruktor +- kopírovací operátor `Trida& operator=(const Vector& other)` + +Kopírovací operátor se stará o přiřezaní do objektu, pokud **již existuje**. Kopírovací konstruktor dělá nový objekt již z existujícího objektu, kopíruje do jiného objektu. Destruktor se stará o správné uvolnění paměti. + +**Rule of five:** rozšiřuje funkční objekt ještě o: +- move konstruktor +- move operátor `Trida& operator=(Vector&& other)` + +Rozšiřuje pravidlo o části spojené s **rvalue**. + +**Celé toto se dělá hlavně pro nějakou správnou práci s dynamickou pamětí v objektech.** ### 23) Jaký je tvar definice lambda funkce v C++? Uveďte příklad a popište, co funkce dělá. +Lambda funkce jsou způsob jak zkrátit kód a udělat ho efektivnější a čitelnější. Jejich syntax je takový: + +```cpp +[capture](parametry) -> navratovy_typ {telo funkce}; + +//ale zkráceně i: +[nic](parametry){telo}; + +//napr + +auto lambda = [=](int a, int b)->int{return a + b;} +//zkráceně +auto lambda2 = [](int a, int b) {return a + b;} +//pak +std::cout << lambda(10, 20); +``` + +Obvykle se lambda funkce šikovně ukládá do proměnné. Používá se `auto`, protože to automaticky určí složitý typ funkce lambda napr: `std::function`. Lambda má několik částí +- **capture** -> `[]` určuje jak jsou pro ní viditelné proměnné z okolního scope, např. `[=]` znamená, že vidí všechny členy ve scopu (třeba fce), kde se nachází. Když nic nepotřebuji, tak vynechám. +- **parametry** -> `()` normální argumenty/parametry jako u normálná fuknce. Pro `(int a, char b)` přijímá interger a char. +- **-> typ** -> tím se určuje návratový typ lambda fce, ale kompilátor na to umí přijít sám, tak se to většinou vynechá. +- **tělo** -> `{};` Zde je kód, co lambda provede. + +**Toto téma jsem trochu zpracoval v dokumentech.** # 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. @@ -619,4 +832,4 @@ Jinak je třeba definovat `>` `<` pomocí přetěžování operátorů a nebo do # Otázky proč - proč píšu kopírovací konstruktor a nepřetěžuju operátor =? -- \ No newline at end of file +- jak funguje mělká/hluboká kopie kontejnerů? \ No newline at end of file