Datové typy

Primitivní a objektové datové typy. Statické a dynamické typy. Substituční princip.

Primitivní a objektové datové typy

Primitivní datový typ
Jedná se o datový typ, který je atomický. Tedy slouží jako základní stavební jednotka. Tento typ nelze nijak dále dělit. Zabírá fixní místo v paměti, a to v závislosti na konkrétním typu. Primitivní datové typy se nachází v alokované v oblasti, které říkáme zásobník (stack).

Rozlišujeme několik základních datových typů, podle toho, jakou mohou obsahovat hodnotu a podle toho, jak velká tato hodnota může být (kolik místa zabírají v paměti)

  • int
    • jedná se celočíselný datový typ; v paměti zabírá 32 bitů; může tedy obsahovat číselnou hodnotu, která se vejde do 32 bitů
  • float
    • jde o typ s pohyblivou plovoucí čárkou; používá se pro desetinná čísla s přesností 24 bitů; zbytek (8 bitů) je pro exponent
  • double
    • pokud potřebujeme větší přesnost než float, použijeme double
  • boolean
    • vyjadřuje buď hodnotu true, nebo false; v paměti zabírá minimálně velikost registru (minimálně 8 bitů)
  • long
    • také se mu říká long int; v paměti zabírá 64 bitů
  • char
    • jedná se o typ, který pojme jeden znak; velikost je 8 bitů -> převod hodnoty na znak pomocí UTF-8 nebo ASCII tabulky
  • enum
    • velikost 32 bitů defaultně
    • neboli výčet identifikátorů a jim přiřazených hodnot; jeho velikost je dána největší velikostí datového typu, který enum obsahuje; pokud máme v enumu int a float, tak enum bude velikosti floatu -> 64 bitů
    • v kódu se enum chová jako konstanta
    • v jazyce java např. je prvnímu identifikátoru přiřazena hodnota 0 a záleží na pořadí ve výčtu identifikátorů
  • pointer
    • neboli ukazatel, drží v sobě odkaz do paměti -> adresu; jeho velikost je dána velikostí adresního registru;

Objektový datový typ

Je definovaný uživatelem. Objekt je struktura, která obsahuje proměnné a metody. Potřebuje tolik místa, kolik potřebují proměnné a metody obsažené v objektu.

Zatímco primitivní datové typy nemají žádné metody -> nic neumí, tak objekty umožňují provádět operace voláním metod na instanci objektu -> String má například metodu size(), která vrací jeho délku.

Objektové datové typy podporují dědičnost, všechny objekty dědí ze základní třídy Object.

Objektové datové typy jsou alokované v oblasti paměti, které říkáme halda (heap). Ale reference na tento objekt (pointer), je uložena na zásobníku.

Alokace probíhá pomocí klíčového slova new.

Halda vs Zásobník

Pro drtivou většinu jazyků platí, že pokud je proměnná inicializována pomocí new (objekt či jiná dynamicky alokovaná proměnná), tak se alokuje na heapu. Tedy data, která objekt obsahuje, tak jsou alokovaná na heapu. Samotná proměnná si drží adresu, která ukazuje kde tyto data najdeme a je uložena v zásobníku (pointer).

Jsou zde malé rozdíly v jazycích, uvedeme si jako příklad Javu a C++.

V obou případech máme třídu A

class A{ 
    int var; 
} 

Pokud chceme vytvořit objekt třídy A, tak v Javě platí to, co je uvedené výše.

A object = new A(); // Na heapu se alokuje 32 bitů a na zásobníku prostor pro adresu, která ukazuje, kde se těchto 32 bitů nachází na heapu

V C++ máme na výběr, kde chceme alokovat

A a; //  Jedná se o klasickou proměnnou, int var je alokována na zásobníku
A* a = new A(); //  Jedná se o referenci, proměnná a je na zásobníku a proměnná var je na heapu -> stejné jako v Javě, akorát bez hvězdičky

U C++ ale platí, že s velkou mocí přichází velká zodpovědnost. Zatímco zásobník si uvolňuje paměť sám, tak halda (heap) vyžaduje, aby ji programátor v C++ uvolnit sám ručně. Jakmile přestane existovat reference v zásobníku na paměť inicializovanou na heapu, tak data již nejsou dostupná, ale stále zabírají místo v paměti. (Java toto nemá, používá tzv. Garbage Collector, který uvolňuje paměť, jakmile referenční proměnné opustí scope). Proto v jazyce C++ používáme např. destruktory, abychom tuto paměť zcela uvolnili.

Pěkné video na heap, stack, uložení jednotlivých proměnných, pointery či dynamickou alokaci paměti si můžete pustit zde

Statické a dynamické typy

Statický typ je takový typ proměnné, který dostal přiřazené místo při deklaraci. Velikost tohoto místa nelze změnit za běhu programu. Velikost je známa a dána při kompilaci. Jedinou možností je explicitní přetypování proměnných - tím změníme velikost paměti dedikovanou pro danou proměnou.

    float a = 15;
    int b;

Už při kompilaci je v paměti vytvořeno místo, pro obě proměnné tohoto typu, nezávisle na tom, zdali jsou inicializované. Primitivní datové typy jsou statické.

Dynamický typ je takový typ proměnné, které může být alokováno místo za běhu programu - runtime alokace.

    A object;

    ///// O nějakou dobu později (program celou dobu běží.....)
    
    object = new A();

Nebylo známo, kolik místa bude proměnná tohoto typu potřebovat při kompilaci. Toto místo ji bylo přiřazeno až při běhu programu. Objektové datové typy jsou dynamické. Opětovným voláním object = new A() později můžeme vytvořit nové místo pro tu samou proměnnou. (Reference zůstává v zásobníku, pouze se změní adresní prostor na heapu, na který odkazuje).

Substituční princip

Jedná se o princip OOP který říká, že objekt (zde instance třídy rodiče), může být nahrazen pod-objektem (instancí třídy potomka) aniž by to rozbilo program.

Potomek může nahradit předka, ale předek nemůže nahradit potomka.

  • Potomek má alokovanou paměť stejného typu + má něco navíc.
  • Nemůžme program rozbít ve smyslu přístupu do paměti (samozřejmě, že můžeme v potomkovi přepsat metody předka tak, aby program již nefungoval, ale z hlediska přístupu do paměti může potomek vždy nahradit předka)

Zdroje