Základy C++ I 1
Přechod z C na C++ jazyk C++ je nadmnožinou jazyka C z hlediska syntaxe se jedná o velmi podobné jazyky, spolu s dalšími jazyky "céčkovské" rodiny, jako je např. C# každý platný program v C je zároveň platným programem C++ vše, co jste se dosud naučili z jazyka C, můžete využít v C++ jazyk C++ dědí i celou knihovnu jazyka C oproti C však C++ nabízí významný krok novým směrem, kterým je podpora tzv. objektově orientovaného programování 2
Přechod z C na C++ program vypisující řetězec "KUK!!!" na monitor #include <iostream> int main() std::cout << "KUK!!!"; return 0; Rozbor vkládání hlavičkových souborů pomocí příkazu preprocesoru #include, stejně jako v C soubor iostream zajišťuje standardní vstup a výstup (jako stdio.h v C) funkce main má stejný význam jako v C, včetně příkazu return 0; výpis na monitor zajišťován objektem std::cout, následovaným operátorem << (rozbor na další straně) 3
Výpis na monitor - objekt cout na rozdíl od funkce pritf se při použití cout neuvádí formát vypisované proměnné std::cout << "retezec"; // výpis řetězcové konstanty std::cout << 5; // výpis konstanty 5 std::cout << n; // výpis proměnné n std::cout << 'c'; // výpis znaku c výpisy je možné zřetězit za sebe int p = delka * sirka; std::cout << "plocha je " << p << " metru ctverecnich"; odřádkování pomocí znaku '\n' (jako v C) std::cout << "prvni radek\n"; std::cout << "druhy radek"; pomocí manipulátoru std::endl std::cout << "prvni radek" << std::endl; std::cout << "druhy radek"; pozn. použití std::endl rovněž vyprázdní výstupní buffer 4
Výpis na monitor příznak std:: před cout nebo endl říká, že tyto objekty patří do tzv. standardního oboru názvů (namespace - později) není ale nutné psát std:: před každým použitím cout nebo endl, kód lze zjednodušit pomocí příkazu using varianta 1: uvedeme výčet všech objektů, které hodláme používat int main() using std::cout; using std::endl; // teď můžeme používat cout a endl bez std:: cout << "vypisuju bez std::, pohoda..." << endl; return 0; varianta 2: deklarujeme, že budeme používat celý namespace using namespace std; // teď můžeme používat všechny objekty standardního oboru názvů // bez specifikace std:: 5
Čtení z klávesnice pomocí objektu std::cin, který je rovněž deklarován v souboru iostream čtení provádíme s pomocí operátoru >> pro psaní std:: před cin platí to samé jako v případě cout a endl #include <iostream> using namespace std; int main() int a, b; cout << "zadejte dve cela cisla: "; cin >> a; cin >> b; int soucet = a + b; int soucin = a * b; cout << "soucet je " << soucet << ", soucin je " << soucin << endl; return 0; čtení více proměnných můžeme spojit do jednoho příkazu: cin >> a >> b; 6
Čtení z klávesnice načtení jednoho znaku lze provádět funkcí get objektu cin // buďto takto // nebo takto char znak = cin.get(); cin.get(znak); načtení řetězce lze provést pomocí operátoru <<, který ale končí načítání při dosažení prvního bílého znaku (nový řádek, mezera, tabulátor) char retezec[50]; cin >> retezec; načítáme-li řetězec "ahoj", bude vše v pořádku načítáme-li "ahoj kámo", načte se jen "ahoj" načtení celého zadaného řetězce včetně mezer provedeme opět funkcí get funkce kromě řetězce přijímá druhý argument, kterým je maximální počet načítaných znaků char retezec[50]; cin.get(retezec, 50); alternativou pro načtení celého řetězce je funkce getline cin.getline(retezec, 50); ROZDÍLmezi get a getline je v tom, že get zanechává na vstupu znak enter (nový řádek), kterým uživatel zakončil svůj vstup, kdežto getline ho zahodí všechny funkce doplní načtený řetězec ukončovací nulou '\0' 7
Inline funkce funkce uvozené klíčovým slovem inline inline double Vetsi(double a, double b) if (a > b) return a; else return b; při klasickém volání funkce dochází k přerušení chodu volající funkce, provádění se přesune do volané funkce a když ta skončí svou činnost, následuje opět skok do volající funkce u funkce deklarované jako inline se toto neděje, kompilátor místo toho zkopíruje kód funkce přímo do místa volání, takže nedojde ke skokům do/z funkcí, které mírně zpomalují chod programu jako inline má smysl psát pouze velmi krátké funkce (maximálně několik řádek) kompilátor může vlastní optimalizací rozhodnout, zda funkci deklarovanou jako inline bude skutečně jako inline používat 8
Datový typ bool slouží k uchování binární informace pravda / nepravda (v C se k tomuto účelu zpravidla používala proměnná typu int, nabývající hodnot 1/0) bool nabývá dvou možných hodnot: true a false (ty jsou vnitřně opět reprezentovány jako celočíselné proměnné s hodnotami 1 / 0) do proměnné typu bool lze přiřadit výsledky logických výrazů (využívajících relační a logické operátory), lze přiřadit i číselné výrazy, kde 0 znamená false, nenulová hodnota true velikost datového typu bool je obvykle 1 bajt int cislo; bool spravnezadani = false; while (spravnezadani == false) cout << "zadejte hodnotu vetsi nez 10, delitelnou tremi: "; cin >> cislo; spravnezadani = cislo > 10 && cislo % 3 == 0; 9
Výčtové typy výčtový typ = datový typ, jež může nabývat jen několika uvedených hodnot vytváříme je klíčovým slovem enum, za kterým následuje název výčtového typu a seznam hodnot ve složených závorkách, vše zakončeno středníkem // definice výčtového typu enum barva cervena, modra, bila, zluta, zelena; // proměnná v programu barva b = modra; smyslem výčtových typů je větší přehlednost programu, je přehlednější používat proměnné s názvem modra nebo bila, než předem dohodnuté kódy barev vnitřně jsou výčtové typy reprezentovány jako celočíselné hodnoty, první z nich má hodnotu 0, každá další pak hodnotu o jedničku vyšší (červená má hodnotu 0, modrá 1,...) vnitřní číslování lze změnit při deklaraci výčtového typu, výčtovým hodnotám lze explicitně přiřadit číselnou hodnotu (opět platí, že následující položka má hodnotu o jedničku vyšší) enum barva cervena, modra = 10, bila, zluta = 20, zelena; cervena má hodnotu 0, modra 10, bila 11, zluta 20, zelena 21 10
Správa paměti na haldě Alokace jednoduché proměnné alokaci proměnných na haldě provádíme pomocí klíčového slova new operátor new vrací adresu alokované paměťi, kterou ukládáme do pointeru, práce s pointery je pak naprosto stejná jako v jazyce C int * pint = new int; double * pdouble = new double; pokud operátor new nemůže alokovat paměť, vrací NULL if (pint == NULL) cout << "alokace pameti selhala"; Alokace pole provádíme stejně jako alokaci jednoduché proměnné // pole 10 int na haldě int * pole = new int[10]; // vynulování všech položek for (int i = 0; i < 10; i++) pole[i] = 0; 11
Správa paměti na haldě Uvolnění paměti provádíme pomocí operátoru delete // alokace proměnné int na haldě int * pint = new int; // uvolnění paměti delete pint; // je vhodné nastavit pointer na nulu, pokud ho hned nebudeme potřebovat pint = NULL; pokud bylo alokováno pole, uvolnění paměti provedeme pomocí operátoru delete[] (operátor delete následovaný prázdnými hranatými závorkami) // pole 10 int na haldě int * pole = new int[10]; // uvolnění paměti delete[] pole; 12
Pointer na pole, pole pointerů čím se liší tato tři pole? int polea[100]; int * poleb = new int[100]; int * polec[100]; for (int i = 0; i < 100; i++) pocec[i] = new int; polea je pole 100 proměnných typu int (polea je tedy pointer na jeho první prvek), pole je alokované na zásobníku, paměť bude uvolněna automaticky pole B je pole 100 proměnných typu int, alokované na haldě, paměť je nutné uvolnit: delete[] pole; polec je pole 100 pointerů (ukazatelů) na typ int, pointery jsou na zásobníku, ale proměnné jsou alokovány na haldě, paměť je nutné uvolnit v cyklu: for (int i = 0; i < 100; i++) delete pocec[i]; 13
Předávání parametrů funkcí odkazem jazyk C umožňuje předávat proměnné do funkcí odkazem prostřednictvím pointerů, což lze provádět naprosto stejně i v jazyce C++ void ZamenaPromennych(int * pa, int * pb) int pomocna = *pa; *pa = *pb; *pb = pomocna; práce s pointery je však poněkud nepříjemná, protože dereference mohou být zdrojem chyb, množství dereferencí může vést k nepřehlednému kódu C++ nabízí alternativu - tzv. ODKAZY, které stejně jako pointery umožňují funkcím měnit obsah předávaných proměnných, ale s mnohem jednodušší syntaxí 14
Odkazy odkaz (tzv. alias) je jiné jméno pro proměnnou, na kterou ukazuje odkaz definujeme napsáním typu proměnné, operátoru adresy & a jména odkazu zároveň mu ihned při definici musíme dosadit proměnnou (inicializovat ho): int P = 10; // proměnná P int & odkaz = P; // odkaz na proměnnou P význam odkazů je v tom, že kdykoliv se použije odkaz, použije se ve skutečnosti proměnná, na kterou odkazuje odkaz = 15; // totéž jako P = 15 odkaz = jinapromenna; // totéž jako P = jinapromenna, tento řádek tedy // nepřiřadí odkazu novou proměnnou // odkaz nemůže odkazovat na nic jiného než na // proměnnou, kterou byl inicializován // adresa odkazu je stejná jako adresa proměnné, na kterou ukazuje cout << "adresa P: " << &P << endl; cout << "adresa odkazu: " << &odkaz << endl; 15
Odkazy jako parametry funkcí odkazy se často používají pro předávání parametrů funkcí umožňují dosáhnout stejného efektu jako pointery (změna proměnné ve funkci se promítne i navenek), ale s jednodušší syntaxí - ve funkci nemusíme provádět dereference void ZamenaPromennych(int & a, int & b) int pomocna = a; a = b; b = pomocna; volání funkce v programu probíhá s použitím proměnných, ne jejich adres ZamenaPromennych(x, y); kompilátor z deklarace funkce ví, že pracuje s odkazy, nepořizuje si tedy lokální kopie parametrů, ale pracuje přímo s předanými proměnnými 16
Odkazy jako parametry funkcí odkazy mohou být konstantní (stejně jako pointery), čehož dosáhneme uvedením klíčového slova const void Funkce(const int & x); jakýkoliv pokus o změnu parametru x uvnitř funkce (např. příkaz x++;) povede k chybě v době kompilace pozn. zatímco u pointerů mohlo být slovo const na dvou místech (konstantní pointer nebo konstantní proměnná), v případě odkazů může být jako konstantní deklarována jen proměnná, protože odkaz je konstantní sám o sobě (nemůže odkazovat na nic jiného) odkazy se jako parametry funkcí (stejně jako pointery) používají proto, aby 1. funkce mohla měnit předávané proměnné 2. funkce mohla "vracet" více hodnot najednou (prostřednictvím parametrů, které změní) 3. funkce šetřila čas a paměť tím, že si nebude pořizovat kopie dat (např. velkých struktur) (tady se často používají konstantní odkazy, pokud chceme mít zaručeno, že funkce data nezmění) 17
Složené datové typy do jazyka C++ je (stejně jako do C) zabudováno několik standardních typů proměnných (short, int, long, float, double, char...) jazyk C nabízí možnost tvořit nové datové typy - struktury (pomocí klíčového slova struct) typedef struct obdelnik int vyska, sirka; OBDELNIK; Nevýhody struktur struktury a funkce, které s nimi pracují, nejsou spojenými celky struktura obsahuje data, ale sama o sobě "nic neumí" abychom mohli měnit data struktury, musíme zavolat nějakou "cizí" funkci, která má strukturu jako parametr data struktur mohou být funkcemi libovolně měněna, koordinace mnoha funkcí pracujících se strukturou může být složitá, program se obtížně spravuje a rozšiřuje 18
Třída třída (class) v sobě sdružuje data popisující nějakou věc, spolu s funkcemi, které tato data spravují a umožňují komunikovat s okolím jasně definovaným a kontrolovaným způsobem data třídy (členská data, členské proměnné) definují, jaké informace jsou ve třídě uloženy bývají (mají být) soukromá (private), tzn. neměla by být přístupná zvenku třídy přístup k datům bývá zajištěn prostřednictvím funkcí třídy funkce třídy (členské funkce, metody) definují, co třída může dělat funkce bývají veřejné (public), tzn. mohou být volány vnějšími částmi programu část funkcí obvykle umožňuje spravovat data třídy, těm se říká přístupové funkce (metody) spojením dat a funkcí do jednoho celku nazýváme ZAPOUZDŘENÍ konkrétní proměnnou nějaké třídy nazýváme OBJEKT 19
Deklarace třídy class obdelnik public: // přístupové metody int VratVysku(); void NastavVysku(int v); int VratSirku(); void NastavSirku(int s); Deklarace obsahuje výčet datových položek třídy deklarace funkcí třídy a je zakončena středníkem. // další funkce void Nakresli(); private: int vyska, sirka; ; seznam veřejných položek je uveden klíčovým slovem public, vše dále uvedené (data i funkce) je veřejné, až do klíčového slova private, které deklaruje začátek seznamu soukromých položek pokud typ přístupu není uveden, jsou položky automaticky soukromé funkce mohou být i soukromé, stejně tak data mohou být veřejná, obvykle tomu tak ale není 20
Definice funkcí třídy definice píšeme jako kterékoliv jiné definice funkcí, pouze před název funkce píšeme název třídy a dvě dvojtečky // funkce pro zjištění výšky int obdelnik::vratvysku() return vyska; // funkce pro nastavení výšky, obsahuje ochranu proti nastavení záporné hodnoty void obdelnik::nastavvysku(int v) if (v >= 0) vyska = v; // funkce pro kreslení obdléníka void obdelnik::nakresli() for (int i = 0; i < vyska; i++) for (int j = 0; j < sirka; j++) cout << '*'; cout << endl; 21
Použití třídy v programu přístup k položkám třídy provádíme pomocí operátoru tečky (jako u struktur) int main() // proměnná typu obdélník s názvem O obdelnik O; // nastavení dat pomocí přístupových metod O.NastavVysku(5); O.NastavSirku(12); // nakreslení vytvořeného obdélníka O.Nakresli(); return 0; soukromá data nelze v programu nastavovat přímo, k tomu jsou určeny přístupové metody, pokud bychom do programu napsali O.sirka = 3;, obdrželi bychom chybu v době kompilace 22
Konstantní metody třídy členskou funkci lze deklarovat jako konstantní, což znamená, že funkce nemění členská data provedeme to klíčovým slovem const, které se píše za kulaté závorky seznamu parametrů // funkce pro zjištění hodnoty datové položky sirka // pouze vrací hodnotu členské proměnné, přitom nijak nemění data // měla by tedy být deklarována jako konstantní // deklarace int VratSirku() const; // definice int obdelnik::vratsirku()const return sirka; 23
Konstruktor je metoda třídy, která je volaná vždy, když se vytváří objekt třídy pokud vytvoříte objekt příkazem obdelnik O; zavolá se konstruktor třídy obdélník a vyhradí paměť potřebnou pro uložení dat velikost alokace bude rovna součtu velikosti jednotlivých datových položek jednotlivé datové položky budou mít náhodnou hodnotu konstruktor si můžeme napsat vlastní a nastavit tak výchozí hodnoty datových členů konstruktor je metoda třídy, která má stejný název jako třída sama nemá žádnou návratovou hodnotu (ani void) // deklarace konstruktoru (v deklaraci třídy) obdelnik(); // definice konstruktoru obdelnik::obdelnik() vyska = 1; sirka = 1; Nyní budou mít všechny čerstvě vytvořené obdélníky výšku a šířku rovnou 1, dokud jim tato data nezměníme prostřednictvím přístupových metod. 24
konstruktor může mít parametry, třída může mít i více konstruktorů (tzv. přetížení - další přednáška) konstruktoru bez parametrů se říká výchozí konstruktor pokud ke své třídě žádný konstruktor nenapíšeme, kompilátor poskytne výchozí konstruktor sám (tento výchozí konstruktor ale datové členy nijak nenastavuje) Destruktor je metoda volaná vždy, když je objekt třídy mazán (opouští svůj obor platnosti, nebo je odstraňován z haldy příkazem delete) destruktor má stejný název jako třída, před názvem se píše vlnovka ~ (tilda), nikdy nemá návratovou hodnotu, ani žádné parametry // deklarace destruktoru Vlastní destruktor má smysl psát u tříd, které ~obdelnik(); pro své datové členy alokují paměť na haldě. // definice destruktoru Úkolem destruktoru je tuto paměť uvolnit obdelnik::~obdelnik() // v tomto destruktoru se nic neděje pokud nenapíšeme vlastní destruktor, kompilátor ho poskytne (tento destruktor ale neuvolní paměť alokovanou na haldě, to musíme udělat ve vlastním destruktoru sami) 25
Třída s daty alokovanými na haldě následující příklad ukazuje, jak v konstruktoru alokovat paměť na haldě a v destruktoru tuto paměť zase uvolnit třída čtverec má jediný datový člen délku strany, který je alokován na haldě, třída udržuje pointer na tuto položku // DEKLARACE TŘÍDY class ctverec public: // konstruktor, destruktor ctverec(); ~ctverec(); // další funkce... private: int *strana; ; // DEFINICE KONSTRUKTORU // alokace paměti pro datový člen ctverec::ctverec() strana = new int; // DEFINICE DESTRUKTORU // uvolnění alokované paměti ctverec::ctverec() delete strana 26
Inline členské funkce členskou funkci lze deklarovat jako řádkovou, stejně jako jiné funkce // deklarace (uvnitř deklarace třídy) inline int VratVysku() const; // definice inline int obdelnik::vratvysku() const return vyska; funkce bude řádková automaticky, pokud její definici umístíte přímo do deklarace třídy // deklarace funkce spojená s její definicí (uvnitř deklarace třídy) int VratVysku() const return vyska; za definicí funkce není středník příkaz uvnitř složených závorek je ukončen středníkem 27