Sdílet prostřednictvím


C++ – systém typů (moderní verze jazyka C++)

Pojem typu je v jazyce C++ velmi důležitý.Všechny proměnné, argumenty funkcí a návratové hodnoty funkcí musí mít typ, aby bylo možné je zkompilovat.Každému výrazu (včetně hodnot literálů) navíc kompilátor před vyhodnocením implicitně přidělí typ.Mezi typy patří int pro ukládání integrálních hodnot, double pro ukládání hodnot s plovoucí desetinnou čárkou (označují se také jako skalární datové typy) nebo standardní knihovna tříd std::basic_string pro ukládání textu.Můžete vytvořit vlastní typ definováním class nebo struct.Typ určuje velikost paměti, kterou chcete přidělit proměnné (nebo výsledku výrazu), druhy hodnot, které mohou být v dané proměnné uloženy, způsob interpretace těchto hodnot (jako vzorců bitů) a operace, které lze s proměnnou provést.Tento článek obsahuje přehled hlavních funkcí systému typů v jazyce C++.

Terminologie

Proměnná: Symbolický název množství dat, který lze používat pro přístup k datům, na která proměnná odkazuje, v rámci rozsahu kódu, kde je definována.V jazyce C++ se pojem „proměnná“ obecně používá k označování instancí skalárních datových typů. Instance ostatních typů se obvykle nazývají „objekty“.

Objekt: Kvůli jednoduchosti a přehlednosti pojmem „objekt“ v tomto článku rozumíme jakoukoli instanci třídy nebo struktury. V obecném smyslu tento pojem zahrnuje všechny typy, včetně skalárních proměnných.

Typ POD (plain old data): Tato neformální kategorie datových typů jazyka C++ odkazuje na skalární typy (viz oddíl Základní typy) nebo na typy třídy POD.Třída POD nemá žádné statické datové členy, které nejsou rovněž POD, a nemá žádné uživatelem definované konstruktory, destruktory ani operátory přiřazení.Třída POD navíc nemá žádné virtuální funkce, žádnou základní třídu a žádné soukromé ani chráněné nestatické datové členy.Typy POD se často používají pro výměnu externích dat, například s modulem napsaným v jazyce C (který má pouze typy POD).

Určení typů proměnných a funkcí

C++ je silně typovaný jazyk a je také staticky zadávaný. Každý objekt má typ, který se nikdy nemění (nepleťte si se statickými datovými objekty).
Pokud v kódu deklarujete proměnnou, musíte explicitně zadat její typ nebo použít klíčové slovo auto, abyste instruovali kompilátor k odvození typu proměnné z inicializátoru.
Pokud v kódu deklarujete funkci, je nutné zadat typ každého argumentu a jeho návratovou hodnotu, případně hodnotu void, pokud funkcí není vrácena žádná hodnota.Výjimkou je použití šablon funkce, které umožňují použití argumentů libovolných typů.

Po prvotním deklarování proměnné nelze její typ později změnit.Můžete však zkopírovat hodnotu proměnné nebo návratové hodnoty funkce do jiné proměnné jiného typu.Tyto operace se označují jako převody typů. Někdy jsou nezbytné, ale nesou s sebou potenciální riziko ztráty dat nebo nepřesnosti.

Po deklarování proměnné typu POD důrazně doporučujeme ji inicializovat, což znamená, že jí přiřadíte počáteční hodnotu.Dokud proměnnou neinicializujete, má hodnotu „garbage“, která se skládá z jakýchkoli bitů, které se původně nacházely v daném umístění v paměti.To je důležitý aspekt jazyka C++. Nezapomeňte na něj zejména v případě, že přecházíte z jiného jazyka, který zpracovává inicializaci za vás.Při deklarování proměnné jiného typu třídy než POD zpracovává inicializaci konstruktor.

Následující příklad ukazuje některé jednoduché deklarace proměnných s popisem každé z nich.Příklad také ukazuje, jak kompilátor používá informace o typu k tomu, aby povolil nebo zakázal určité následné operace s proměnnou.

    int result = 0;              // Declare and initialize an integer.
    double coefficient = 10.8;   // Declare and initialize a floating 
                                 // point value.
    auto name = "Lady G.";       // Declare a variable and let compiler 
                                 // deduce the type.
    auto address;                // error. Compiler cannot deduce a type 
                                 // without an intializing value.
    age = 12;                    // error. Variable declaration must
                                 // specify a type or use auto!
    result = "Kenny G.";         // error. Can’t assign text to an int.
    string result = "zero";      // error. Can’t redefine a variable with
                                 // new type.
    int maxValue;                // Not recommended! maxValue contains 
                                 // garbage bits until it is initialized.

Základní (vestavěné) typy

Na rozdíl od některých jazyků nemá C++ žádný univerzální základní typ, z něhož by byly odvozeny všechny ostatní typy.Implementace Visual C++ obsahuje mnoho základních typů, označovaných také jako předdefinované typy.Patří mezi ně číselné typy jako int, double, long, bool, a dále typy char a wchar_t určené pro znaky ASCII a UNICODE.Většina základních typů (s výjimkou bool, double, wchar_t a souvisejících typů) má svou nepodepsanou verzi, která upravuje rozsah hodnot, jež může proměnná ukládat.Například typ int, který ukládá 32bitové celé číslo se znaménkem, může představovat hodnotu od -2 147 483 648 do 2 147 483 647.Hodnota unsigned int, která se také ukládá jako 32bitová, může ukládat hodnotu od 0 do 4 294 967 295.Celkový počet možných hodnot je ve všech případech stejný, liší se pouze rozsah.

Základní typy jsou rozpoznávány kompilátorem, který má vestavěná pravidla určující, jaké operace lze s jednotlivými typy provádět a jak je lze převést na jiné základní typy.Úplný seznam předdefinovaných typů včetně velikostí a číselné limitů naleznete v tématu Základní typy (C++).

Následující ilustrace znázorňuje relativní velikosti předdefinovaných typů:

Velikost v bajtech vestavěné typy

V následující tabulce jsou uvedeny nejčastěji používané základní typy:

Typ

Velikost

Komentář

int

4 bajty

Výchozí volba pro integrální hodnoty.

double

8 bajtů

Výchozí volba pro hodnoty s plovoucí desetinnou čárkou.

bool

1 bajt

Představuje hodnoty, které mohou být „true“ nebo „false“.

char

1 bajt

Používá se pro znaky standardu ASCII ve starších řetězcích stylu C nebo objektů std::string, které nikdy nebude nutné převést do kódování UNICODE.

wchar_t

2 bajty

Představuje hodnoty „širokých“ znaků, které mohou být kódovány ve formátu UNICODE (UTF-16 v systému Windows, v jiných operačních systémech se může lišit).Jedná se o typ znaku, který se používá v řetězcích typu std::wstring.

unsigned char

1 bajt

Jazyk C++ nemá žádný předdefinovaný typ byte. Umožňuje znázornit hodnotu bajtu pomocí unsigned char.

unsigned int

4 bajty

Výchozí volba pro bitové příznaky.

long long

8 bajtů

Představuje hodnoty tvořené vysokým celým číslem.

Typ void

Typ void je speciální typ; nemůžete deklarovat proměnnou typu void, můžete však deklarovat proměnnou typu void * (ukazatel na void), což je někdy nezbytné při přidělování nezpracované (netypové) paměti.Odkazy na void nicméně nejsou typově bezpečné a obecně se jejich použití v moderním jazyce C++ silně nedoporučuje.V deklaraci funkce návratová hodnota void znamená, že funkce nevrátí hodnotu. Toto je běžné a přijatelné použití typu void.Jazyk C vyžaduje pro deklaraci typu void v seznamu parametrů (například fou(void)) funkce s nulovými parametry. V moderním jazyce C++ se ovšem tento postup nedoporučuje. V tomto případě je vhodné použít deklaraci fou().Další informace naleznete v tématu Převody a bezpečnost typů (moderní verze jazyka C++).

Kvalifikátor typu const

Jakýkoli vestavěný nebo uživatelem definovaný typ může být kvalifikován pomocí klíčového slova const.Členské funkce mohou navíc mít kvalifikaci const a dokonce i přetížení const.Hodnotu typu const nelze po inicializaci změnit.

    const double PI = 3.1415;
    PI = .75 //Error. Cannot modify const variable.

Kvalifikátor const se běžně používá v deklaracích funkcí a proměnných a „správnost const“ představuje v jazyce C++ důležitý koncept. V podstatě znamená použití parametru const v době kompilace k zajištění toho, aby nedošlo k neúmyslné úpravě hodnot.Další informace naleznete v tématu const (C++).

Typ const se liší od své nekonstantní verze; například const int je typ odlišný od typu int.Operátor C++ const_cast můžete použít v těch výjimečných případech, kdy je třeba odebrat z proměnné const-ness.Další informace naleznete v tématu Převody a bezpečnost typů (moderní verze jazyka C++).

Typy řetězců

Přísně vzato nemá jazyk C++ vestavěný typ „řetězec“. Typy char a wchar_t ukládají jednotlivé znaky – pro napodobení řetězce je třeba deklarovat více těchto typů a přidat ukončující hodnotu null (například ASCII ‘\0’) k prvku pole za posledním platným znakem (postup známý také jako „řetězce ve stylu jazyka C“).Psaní řetězců ve stylu C vyžadovalo zapsat mnohem více kódu nebo využít funkce externí knihovny pro tvorbu řetězců.V moderním jazyce C++ však existují typy standardní knihovny std::string (pro 8bitové řetězce znaků typu char) nebo std::wstring (pro 16bitové řetězce znaků typu wchar_t).Tyto kontejnery STL lze považovat za nativní typy řetězce, protože jsou součástí standardních knihoven, které jsou zahrnuty ve všech prostředích sestavení kompatibilních s jazykem C++.Tyto typy v aplikaci jednoduše zpřístupníte pomocí směrnice #include <string>. (Pokud používáte knihovnu MFC nebo ATL, je k dispozici také třída CString, ta ale není součástí standardu C++.) Použití polí znaků zakončených hodnotou null (výše zmíněné řetězce ve stylu C) se v moderním jazyce C++ důrazně nedoporučuje.

Uživateli definované typy

Při definování typů class, struct, union nebo enum bude tato konstrukce použita ve zbytku kódu, jako by šlo o základní typ.Má známou velikost v paměti a na kontrolu doby kompilace a doby trvání programu při běhu se vztahují některá pravidla týkající se způsobu jeho použití.Nejdůležitější rozdíly mezi základními typy a typy definovanými uživateli jsou následující:

  • Kompilátor neobsahuje žádné předdefinované informace o uživatelském typu.Typ se „učí“, když se během kompilace poprvé setká s jeho definicí.

  • Jako uživatel určujete, jaké operace lze s vaším typem provádět a jak ho lze převést na další typy. K tomu definujete (pomocí přetížení) příslušné operátory, buď jako členy třídy nebo jako nečlenské funkce.Další informace naleznete v tématu Přetížení.

  • Není nutné zadávat typy staticky (pravidlo, že typ objektu se nikdy nemění).Prostřednictvím mechanismů dědičnosti a polymorfismu může mít proměnná definovaná jako uživatelem definovaný typ třídy (dále jako instance objektu třídy) jiný typ v době běhu a jiný v době kompilace.Další informace naleznete v tématu Odvozená třída.

Typy ukazatelů

Jazyk C++ přejímá vlastnost platnou od nejstarší verze jazyka C a i nadále umožňuje deklarovat proměnnou typu ukazatel pomocí speciálního deklarátoru * (hvězdička).Typ ukazatele ukládá adresu umístění v paměti, kde je uložena skutečná hodnota dat.V moderním jazyce C++ jsou tyto ukazatele označovány jako nezpracované a jsou v kódu přístupné pomocí speciálních operátorů * (hvězdička) nebo -> (pomlčka s větší-než).Tento postup se nazývá zrušení reference. To, který operátor použijete, závisí na tom, zda rušíte referenci ukazatele na skalár nebo na člen v objektu.Práce s typy ukazatelů byla dlouhou dobu jedním z nejsložitějších a nejvíce matoucích aspektů vývoje programů v jazycích C a C++.Tato část popisuje některé skutečnosti a postupy, které vám pomohou při používání nezpracovaných ukazatelů, pokud je chcete využívat. V moderním jazyce C++ však již není nutné (a ani se nedoporučuje) používat pro vlastnictví objektů nezpracované ukazatele. Mnohem vhodnější jsou inteligentní ukazatele (podrobněji se jim věnujeme na konci této části).Nezpracované ukazatele lze stále s výhodou a bezpečně používat ke sledování objektů, pokud je však potřebujete použít pro vlastnictví objektu, měli byste tak činit s rozvahou a velmi pečlivě promyslet, jak se budou objekty vlastněné těmito ukazateli vytvářet a odstraňovat.

Základní princip, který byste měli znát, je, že deklarací proměnné nezpracovaného ukazatele se přidělí pouze paměť potřebná k uložení adresy umístění v paměti, na které bude ukazatel odkazovat při zrušení reference.Přidělení paměti pro samotnou hodnotu dat (tzv. záložní úložiště) se v tomto okamžiku nepřidělí.Jinými slovy, deklarováním proměnné nezpracovaného ukazatele vytváříte proměnnou adresy v paměti, nikoli skutečnou datovou proměnnou.Zrušení reference na proměnnou ukazatele, aniž byste se ujistili, že proměnná obsahuje platnou adresu záložního úložiště, způsobí v programu nedefinované chování (obvykle závažnou chybu).Následující příklad ukazuje tento druh chyby:

    int* pNumber;       // Declare a pointer-to-int variable.
    *pNumber = 10;      // error. Although this may compile, it is
                        // a serious error. We are dereferencing an
                        // uninitialized pointer variable with no
                        // allocated memory to point to.

V příkladu dochází ke zrušení reference na typ ukazatele bez přidělení paměti, kde by byla uložena skutečná celočíselná data, a bez přidělení platné adresy v paměti.Následující kód tyto chyby opravuje:

    int number = 10;          // Declare and initialize a local integer
                              // variable for data backing store.
    int* pNumber = &number;   // Declare and initialize a local integer
                              // pointer variable to a valid memory
                              // address to that backing store.
...
    *pNumber = 41;            // Dereference and store a new value in 
                              // the memory pointed to by
                              // pNumber, the integer variable called
                              // “number”. Note “number” was changed, not
                              // “pNumber”.

V příkladu s opraveným kódem se úložiště záloh, na které odkazuje parametr pNumber, vytvoří pomocí místního zásobníku paměti.Pro jednoduchost používáme základní typ.V praxi tvoří záložní úložiště pro ukazatele nejčastěji uživatelem definované typy, které jsou dynamicky přiřazovány do oblasti paměti pojmenované halda (nebo „volné úložiště“) pomocí výrazu klíčového slova new (v programování ve stylu C se používala starší funkce knihovny prostředí runtime malloc()).Po přidělení jsou tyto „proměnné“ obvykle označovány jako „objekty“, zejména v případě, že jsou založeny na definici třídy.Paměť přidělená k parametru new musí být odstraněna odpovídajícím výrazem delete (případně, pokud jste k přidělení použili funkci malloc(), funkcí běhového prostředí C free()).

Snadno se však stane – zejména ve složitém kódu – že zapomenete dynamicky přiřazované objekty odstranit. To pak způsobí chybu prostředků nazývanou nevracení paměti.Z tohoto důvodu se používání nezpracovaných ukazatelů v moderním jazyce C++ nedoporučuje.Téměř vždy je lepší obalit nezpracovaný ukazatel inteligentním ukazatelem, který automaticky uvolní paměť při vyvolání jeho destruktoru (když se kód dostane mimo rozsah pro inteligentní ukazatele). Díky inteligentním ukazatelům se ve svých programech C++ prakticky vyvarujete celé jedné třídy chyb.V následujícím příkladu předpokládejme, že MyClass je typ definovaný uživatelem, který má veřejnou metodu DoSomeWork();

void someFunction() {
    unique_ptr<MyClass> pMc(new MyClass);
    pMc->DoSomeWork();
}
  // No memory leak. Out-of-scope automatically calls the destructor
  // for the unique_ptr, freeing the resource.

Další informace o inteligentních ukazatelích naleznete v tématu Chytré ukazatele (moderní verze jazyka C++).

Další informace o převodech ukazatelů naleznete v tématu Převody a bezpečnost typů (moderní verze jazyka C++).

Další informace o ukazatelích obecně naleznete v tématu Ukazatelé.

Datové typy ve Windows

V klasickém programování v systému Win32 v jazycích C a C++ používá většina funkcí makra TypeDef a #define specifická pro systém Windows (definovaná v windef.h) a určující typy parametrů a návratové hodnoty.Tyto „datové typy systému Windows“ jsou většinou pouze zvláštní názvy (aliasy) přidělené předdefinovaným typům v jazycích C/C++.Úplný seznam těchto funkcí typedef a definičních souborů preprocesoru naleznete v části Windows Data Types.Některé tyto funkce typedef, jako např. HRESULT a LCID, jsou užitečné a mají popisný charakter.Jiné, například INT, nemají zvláštní význam a představují pouze aliasy základních typů jazyka C++.Další datové typy systému Windows si zachovaly názvy pocházející z dob programování v jazyce C a 16bitových procesorů a ve světě moderního hardwaru a operačních systémů již nemají místo ani smysl.Existují také speciální datové typy přidružené ke knihovně prostředí Windows Runtime uvedené jako Windows Runtime base data types.V moderním jazyce C++ platí obecná rada preferovat základní typy C++, pokud typ Windows nesděluje některý další význam o tom, jak má být daná hodnota interpretována.

Další informace

Další informace o systému typů v jazyce C++ naleznete v následujících tématech.

Typy hodnot (moderní verze jazyka C++)

Popisuje typy hodnot a problémy týkající se jejich použití.

Převody a bezpečnost typů (moderní verze jazyka C++)

Popisuje běžné problémy při převodu typů a ukazuje, jak se jim vyhnout.

Viz také

Další zdroje

C++ vás vítá zpět (moderní verze jazyka C++)

Referenční dokumentace jazyka C++

Standardní knihovna C++ – referenční dokumentace