# SZZ Programování
## [Naše starý repo o prográmku](https://github.com/mpicek/programko)
# OOP pojmy
### Virtualni metoda
- ma implementaci, ale muzu ji overridovat (prepsat ji implementaci v potomkovi)
- napr v jave nebo pythonu jsou vsechny metody virtualni, staticke metody ale virtualni nejsou. V jinych jazycich se virtualni metody definuji jako virtualni pomoci `virtual`
#### Override
- **prepsani** metody v potomkovi
#### Overload
- **pretizeni** metody - volani s ruznymi parametry
### Abstraktni metoda
- nekdy jsem pro ni videl taky nazev "pure virtual" nebo tak neco
- nema implementaci, musim ji implementovat v potomkovi (tzn. predefinoval jsem ji, je tudiz taky virtualni)
### Abstraktni trida
- kdyz ma trida alespon jednu abstraktni metodu, tak je cela trida abstraktni
- kdyz dedim z abstraktni tridy, musim implementovat **vsechny** abstraktni metody!
- v cpp myslim je abstraktni trida vlastne to samy jako interface, hlavne proto, ze v cpp je vicenasobna dedicnost, takze jakoby "implementuju" vic abstraktnich trid (coz je to samy jako implementace vice interfacu)
- v jave ale neni vicenasobna dedicnost, takze muzu dedit jen od jedny tridy (treba i abstraktni), na druhou stranu muzu implementovat interfacu kolik chci :)
- jsou tam i jiny dalsi maly rozdily, ze v interfacu je vsechno public, static, final, to v abstraktni tride neni a taak
### Anonymni metoda
- je to trida, ktera nema jmeno, je pouzita nekde v kodu jen na jednom miste treba
### Typy dedicnosti
- **jednoducha** (muzu dedit max od jedny tridy) - Java, vicenasobnou dedicnost zaridim pres interface
- **vicenasobna** - ze muzu dedit od vice trid naraz - C++, Python, muze nastat ale ten diamond problem, kdy zdedim tridu dvakrat
### Polymorfismus
- k tomu, abych mel treba pole s objekty zvire a do toho mohl ulozit potomky ty tridy (krava, pes, ..)
### Tabulka virtualnich metod
- v objektu je promenna ukazujici na tuhle tabulku
- ta tabulka je pole ukazatelu na virtualni funkce
- je jedna pro kazdou tridu
- pri volani metody se zavola ta, ktera je v tyhle tabulce ulozena
- objekt obsahuje odkaz na tuhle tabulku (a tenhle odkaz tam dosadi konstruktor)
### Interface
- je to takova smlouva - rika se v ni, co bude mit definovano potomek
- je to jako seznam funkci
- ty funkce nejsou definovany, je jako popsane rozhrani, ale implementace neni definovana (ale muze byt defaultni treba v jave)
- je to proto, ze neni nasobna dedicnost
- muzeme tedy rict, ze prvek splnuje libovolny pocet interfacu a implementovat je v nem
## Práce s prostředky a mechanizmy pro ošetření chyb
### Chyby
Chyby dělíme na:
- syntaktické (synax error)
- špatný zápis podle pravidel programu
- hlásí je překladač před spuštěním programu
- např. špatné mezery v Pythonu, chybějící složená závorka,
neexistující typ ...
- běhové (vyjímky = exception) (runtime error)
- nepřípustné operace
- ohlásí je program za běhu (až k nim dojde (pokud dojde))
- sémantické (semantic error)
- program vydá nesprávný výsledek, neskončí ...
- nejsou hlášeny (musí je kontrolovat uživatel)
Proces odstraňování se nazývá ladění.
### Vyjímky
- vyjímka (exception) = zpráva o běhové chybě
- je objekt - typ záleží na programovacím jazyku
- Python - vždy potomci třídy BaseException (NameError, TypeError,
...)
- lze také odvodit vlastní typy
- C++ -
- C# - vždy potomci System.Exception
- obsahuje i další informace (text zprávy, místo chyby, celý zásobník
volání)
#### Ošetření vyjímek
Try-catch (resp. try-except) struktura:
- v try bloku kontrolujeme zda nastala chyba
- catch blok ošetřuje specifikovanou vyjímku (může jich být více) -
provede specifické příkazy pokud nastala vyjímka
- catch umí odchytit i všechny vyjímky - není to vhodné, chceme pouze
odchytit chyby, kterým rozumíme (může nastat nečekaná chyba, o které
bychom se nikdy nedozvěděli)
- objekt reprezentující chybu lze typicky uložit do proměnné a
kontrolovat vlastnosti vyjímky (jméno, textový výpis, argumenty,
zásobník volání)
- StackTrace - jak vypadal GC zásobník v místě vzniku vyjímky, kde
vyjímka vznikla
- používat pouze vyjimečně - drahá operace (musí se uložit informace o
chybě - poměrně obsáhlé + časově náročné)
Finally:
- zapisuje se za try-catch, pro vykonání příkazu bez ohledu zda je
vykonávána část try, nebo catch
- typicky pokud pracujeme se souboru, tak je chceme zavřít (ve
finally)
- problém s tzv. Disponable objekty (a paralelními operacemi)
- při práci se zamknou (aby nebyly modifikovány jinými)
- např. pokud mám otevřen textový editor, pak soubor nelze otevřít
jiným programem
- typicky se odemkne voláním Dispose (u souboru v rámci Close)
- problém pokud nastane vyjímka, tak zůstane soubor zamčen a není
s ním možno pracovat
- odemknout soubor může GC, ale ten zahazuje soubory až pokud je
velký nárok na pamět (nemusí nastat do ukončení programu)
- řešení je použití Dispose ve finally, nebo using (with)
struktura (je tam Dispose interně)
Vyvolání vyjímky throw (Python - raise):
- pokud chceme ohlásit výjimečnou situaci, kterou neumíme řešit
(chceme ukončit program a ohlásit chybu)
- např. funkce načítající číslo nedostane číslo, server neodpovídá,
...
- přeskočí se zbytek bloku (nedokončená funkce), možná se zastaví
někde výše (v catch)
Problematická je práce s databázemi, soubory, .... Řešením je použití
struktury try ...finally, nebo using (resp. with) (try ...finally je
implicitně zahrnuto ve struktuře).
#### Druhy vyjímek - C#
- SystemExceptions - typické bugy (NullReference, IndexOutOfRange),
nechci odchytávat
- ArgumentException - chyby uživatele (OutOfRange, NullArgument)
- OutOfMemoryException
- došlo místo na GC haldě
- nezbývá místo pro vyskakovací okno
- zbývá dostatek místa pro výpis chyby do terminálu
- chci co nejrychleji zabít (abych uvolnil místo pro jiné
programy)
- StackOverflowException
- došla paměť na zásobníku (oproti GC už nezbývá ani kousek
- prostě zabij (není místo na nic)
- nastává jednoduše pro vícevláknové programy (paralelní)
- nebezpečné v rekurzi - hluboké zanoření, musí si pamatovat i
nadřazené proměnné funkcí
- ve funkcionálních jazycích je to vyřešeno koncovou rekurzí
(proměnné jsou každým voláním přepisovány (víme, že už
nadřazenou funkci nebudeme potřebovat))
### Defenzivní programování
- vhodné předcházet chybám
- ověřování vstupu (chyby uživatelů) a výstupů (vlastní chyby)
- testovaní podmínek vstupu a výstupů (invarianty)
- ověřování situací, které nemohou nastat
- nízká tolerance (co neznáme, odmítnout)
### Rozdíly v programovacích jazycích
- rozdíl mezi kompilovanými jazyky a interpretovanými
- v Pythonu probíhá kontrola typů až za běhu (neodhalí překladač)
- ale pomocí externích nástrojů, lze kontrolovat před spuštěním)
a: int = 5
b: str = "abcd"
def delka( slovo: str ) -> int:
return len(slovo)
- kontroluje externí nástroj (Visual Studio, Visual Studion Code,
PyCharm, ...)
### Testování
- výpisy pro vývojáře (printy)
- unit testy - testují jednotlivé části, třídy, funkce
- integrační testy - spolupráce částí programů
- regresní testy - zachování funkčnosti programu (jestli se
aktualizací nerozbil)
- testy nezávislé na okolí (vše si připraví samy)
- Test Driven Developement - nejprve napsat testy (postupně je
splňujeme)
-
## Životní cyklus objektů a správa paměti
Životnost objektu je charakterizována časem mezi vytvořením objektu a
jeho zničením. Objekt může reálně existovat, ale již nemusí být
přístupný (ještě nebyl přepsán). Rozlišujeme proměnné na:
- hodnotové
- každá proměnná je jiná instance objektu (ne pouze reference na
objekt)
- typicky objekty malých tříd (int, char, struckt (v C#))
- např. v C++ jsou všechny proměnné hodnotové (pokud chci
referenční musím specifikovat)
- referenční (silná reference)
- new - alokace na haldě + konstruktor (v C#)
- v proměnné je uložen odkaz na objekt (nekopíruje se celý objekt,
pokud není řečeno přímo)
- typicky větší objekty (string, array, class, ...)
- pointery (slabá reference)
- ukazují libovolně do paměti (nikdo je nekontroluje)
- raw pointery v C++ (v C# komplikovanější)
- musíme ručně kontrolovat, zda byl objekt zničen (řeší smart
pointery v C++)
- tracking reference
- v C# jsou to out typy
- mohou ukazovat na nedeklarované proměnné, ale překladač
kontroluje, jestli se používá již inicializovaný objekt
```{=html}
<!-- -->
```
- mutable - lze měnit jejich hodnotu
- immutable - nelze upravit hodnotu, při změně hodnoty vzniká nový
objekt (nově alokován na GC haldě), např. string, vhodné pro
paralelní programy
- problém se častým string concat (vznikají nové objekty), ale
překladače to mají často vyřešeno efektivně
- C# varianta je StringBuilder (muttable string) - pro časté
úpravy
- readonly - immutable, ale zakázána také modifikace objektu
### Vznik objektů
- objekt je alokován na haldě až po zavolání konstruktoru
- statické parametry mají své vlastní konstruktory - inicializace při
prvním použitím typu
### Uložení v paměti
// TODO: Obrazek
- po definici lokální proměnné se uloží na zásobník (stack)
- po definici objektu třídy jsou jeho proměnné uloženy na haldě
- statické proměnné jsou uloženy na loader heap (společně se strojovým
kódem)
### Životnost lokálních objektů
- proměnná vzniká na začátku bloku (uvnitř složených závorek) a zaniká
po jeho ukončení
- životnost můžeme v C# explicitně zkrátit kódem ve složených
závorkách (navíc)
- proto můžou být na stacku (vždy první proměnná nahoře je ta, co bude
nejdříve smazána)
### Adresový prostor
- počítače mají svůj vlastní adresový prostor, kde jsou uloženy
jednotlivé části kódů
- horní část je typicky vyčleněna pro kernel OS
- dolní část je virtuální adresový prostor (s kterým pracuje uživatel)
- problém s programy využívající více programovacích jazyků (může
dojít paměť), každý má svůj vlastní stack, heap (může rychleji dojít
paměť)
### Alokace paměti
- 1\. část alokace je Reserve - najde zda existuje vhodná volná část
paměti
- 2\. část alokace je Commit - fyzický zápis do paměti (typicky po
částech)
- v C++ není halda spravována GC, je tedy nutno objekt implicitně
smazat příkazem delete
- problém pokud double delete neoprávněný přístup do paměti
(objekt už tam není)
- problém pokud nesmažu plýtvání pamětí (neodalokovanou část nelze
alokovat)
- v C# a dalších GC jazycích spravuje objekty na haldě garbage
colector
- jednou za čas zkontroluje objekty na, které nevedou reference a
smaže je z haldy
- je spouštěn při nové alokaci (ne vždy)
- významně zpomaluje běh programu
- problém je, že na haldě vznikají díry a je potřeba většího
souvislého úseku (např. pro pole) heap compacting
- heap compacting - prochází se halda a objekty se přesunují dolů,
aby zaplnily díry, (je to velmi pomalé, provádí se někdy při
dealokaci)
- pro zefektivnění 2 haldy (Large Object Heap, Small Object
Heap) - velké objekty typicky jen pole (přizpůsobené operace na
haldě)
- pro efektivitu dělíme objekty na 3 generace (dle délky života)
- s rostoucím časem se přeživší objekty posunují do vyšší generace
(není kontrolována GC tak často)
- problém velkých objektů v nejvyšší generaci (např. zavřený
soubor) lze implicitně zavolat GC
## Implementace základních prvků objektových jazyků
Rychle připomenutí OOP
- kód rozdělen do tříd a objektů (přidružen k datům)
- metody zapouzdřeny v objektech -> lepší přenos kódu
- dědičnost
- polymorfizmus - do jednoho pole narvu různé podtřídy dané třídy a mohu volat stejnou metodu na všech
- přetěžování operátorů, přetěžování metod
- rozhraní
Implementace .. tabulka virtuálních metod VS jak napsat objetk, třídu,.. reference vs deep copy
## Nativní a interpretovaný běh, řízení překladu a sestavení programu
#### Nativni
- je to kompilovane do jazyka, kteremu rozumi procesor na dane platforme
- kdyz mam zkompilovanej program na jedny platforme a chci ho prenest na druhou, mam dve moznosti, jak to udelat:
1. zkompilovat kod znova pro tu danou platformu
2. pouzit emulator, kterej na druhy platforme jakoby simuluje CPU toho prvniho stroje a tak si ten program mysli, ze je na tom prvnim stroji, doopravdy ale neni
#### Interpretovany jazyk
- 3 druhy:
1. provádějí přímo zdrojovej kód (unixovej shell)
2. přeloží zdroják do mezikódu a pak ho spustí (Python)
3. přímo spustí předem vytvořený předkompilovaný mezikód, který je produktem části interpretu (python, java prej)
- výhody: dobře se to ladí, je to kompatibilní (když mám interpret na jiný platformě), paměť se dobře spravuje, protože není vázána na fyzickou adresu v operační paměti
- obecně se dá prej mnohem jednodušeji vyrobit interpret než kompilátor -> můžu mít hodně platforem cílovejch
- rychlost se zvysuje diky just-in-time (JIT) kompilaci
- ta funguje tak, ze pri behu programu (uz ho jako interpretuju), tak si prekladam do nativniho jazyka casti toho kodu. Udelam to predem a pak ta interpretace je rychlejsi
- ma to ale overhead na startu, kdy se to predkompilovava, taky to ma overhead uprostred programu, protoze se to kompiluje i za behu
#### Vyhody a nevyhody
- nativni
- rychlejsi
- zavisly na platforme
- interpretovany
- pomalej (ale neni to tak hrozny s JIT)
- vice nezavisle na platforme
- potreba mit interpret na cilovym PC
#### Překlad
- vlastně funkce z jednoho jazyka do jiného (zdrojový kód -> strojový kód)
- kód fragmentován do více jednotek (např. souborů s kódem)
- tyhle jednotky jsou překládány zvlášť (občas třeba nechceme všechno kompilovat naráz, žejo..) => překládají se do tzv. **objektového kódu**
- objektový kód už je v podstatě strojový kód, jen jsou tam navíc informace o externích proměnných a informace umožňující přemístění návěstí..
- Z jednotlivých objektových kódů a z funkcí v knihovnách se pomocí **linkeru** sestaví spustitelný strojový kód
- překladový nástroje pak mohou generovat i něco navíc - mapy proměnných, kód s rozvojem maker, info o vyhodnocení konstant ... tohle se pak může používat pro debuggery
- zadávaj se taky informace o tom, jak má překladač kód optimalizovat, o povolených rozšířeních syntaxe, warningy, ...
- zdroják může obsahovat **pragmy**, což jsou příkazy nebo informace pro kompilátor
#### Překladač
- běžně sestává ze dvou částí
1. frontend = závislá na vstupním jazyce
2. backend = závislá na cílové architektuře
- překlad nemusí být přímý, může používat **mezikód**
- mezikód je pak běžně nezávislý na platformě, ale je to už hezky zpracovanej zdrojovej kód (už je hodně zprocesovanej a je lehčí to už dopřekládat)
- výhody pak jsou, že mezikód pak můžu překládat do různých architektur (a už to není tak složité) a dále že můžu mít více jazyků (zdrojových) a všechny mi to přeloží do mezikódu a pak už tu zbylou část (do cílový architektury) mám hotovou
#### Just-Tn-Time kompilace (JIT)
- Zdrojový kód se **CELÝ** přeloží do mezikódu a to je pak dáno do kompilátoru dalšímu při spuštění programu (prej ne interpretu (na wikipedii)) .. asi proto, že se to překládá do nativního kódu
- kompilační chyby jsou nalezeny již před spuštěním (super!)
- on je danej před spuštěním a ono se to celý přeloží před spuštěním (prodleva na začátku)
- pak jsou ekonomický JIT, který prostě v průběhu kompilujou
- hodně se cachuje
- jde ale líp optimalizovat pro danej procesor, což je gut
- je možnost taky kompilovat při instalaci (Ahead-of-Time kompilace)
- techniky
- odstranění mrtvého kódu
- vkládání těl metod
- odstranění shodných podvýrazů
- rozbalování smyček