dodelane okruhy krom Qt, ale chtelo by to jeste trochu lasky

This commit is contained in:
2026-04-06 18:21:35 +02:00
parent f5951a9159
commit 7440ae1b9a

View File

@@ -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 <typename T>`, ale `template <Class C>` je ekvivalentní.
Můžu taky parametrizovat třeba konstantní hodnotou, např. `tepmlate <int N>` nebo více parametrů `template <typename T, typename U>`
### 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<int(int)>`. 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 =?
-
- jak funguje mělká/hluboká kopie kontejnerů?