Jump to content
  • Announcements

    • Xmat

      Pravidlo pro postování v TTT

      Do sekce Tipy, triky, tutoriály nepatří žádné dotazy.   Postujte sem vaše návody, tipy a různé další věci jež uznáte za vhodné sdělit zdejšímu osazenstvu, ale veškeré dotazy směřujte do sekce Všeobecná diskuse.
    • Replik

      Seznam návodů a důležitých témat v této sekci

      Pro lepší přehlednost jsem vytvořil tento seznam, který vás, méně zkušené, lépe provede touto sekcí. Věřím, že zde najdete, co hledáte. Vypsané jsou návody, které jsou oficiálně uznané jako návody. Běžné diskuze, které neposkytují postupy a rady zvěřejněny nejsou.   Instalace vlastního MaNGOS Serveru Díky těmto návodům budete (měli by jste být) schopni vytvořit a následně spustit váš vlastní server. Nastavení je pro verze s i bez datadisku.   Instalace MaNGOS Serveru (bez datadisku TBC) - Autor Benny Instalace MaNGOS Serveru (s datadiskem TBC) - Autor Malfik Instalace MaNGOS Serveru v prostředí Linux - Autor charlie Instalace MaNGOS Serveru v prostředí Linux - Autor kupkoid   Chyby a jejich řešení při přihlašování k serveru - Autor Cybe   Zálohování uživatelských dat   Dávkový soubor (BAT soubor) pro vytvoření SQL záloh - Autor Replik   Kompilování - tvoření vlastních release (revizí)   Tvorba kompilací pro Win32 (MangoScript) - Autor bLuma   Ostatní - těžko zařaditelné, ale neznamená to, že nejsou dobré   VIP Systém - Autor charlie Tvorba Webových stránek pro MaNGOS - Autor zahuba Tvorba teleportačních NPC (MangoScript) - Autor Replik Registrační web (původně předělaná SPORA) Funkční pro Antrix i MaNGOS - Autor Replik Nastavení a spuštění Minimanager pro MaNGOS - Autor BlackMartin Nastavení MaNGOS Website - Autor Artorius   Samozřejmě jsou zde i jiné návody, ale tyto jsou nejvíce používané, proto věřím, že vám budou nápomocné. Tuto sekci budeme upravovat podle potřeby. Pokud by jste něco nenašli nebo si nevěděli rady, hledejte na fóru a teprve potom založte vlastní topik. Pokud nějaký autor vytvoří kvalitní návod a chtěl by ho zveřejnit i v tomto seznamu, doporučuji, aby mi napsal zprávu skrze PM.   Díky a přeji hezký den na WoWResource   Replik
    • Aristo

      Příspěvky tam, kde nemají co dělat

      Dodržujte zákaz přispívání do topiků s repaky pokud si to zakladatelé nepřejí!! Opakované psaní příspěvků bude trestáno warnem.
    • Aristo

      Používání spoilerů

      Poslední dobou má většina uživatelů fora zvláštní nutkání postovat extrémně dlouhé texty nebo kódy, které zabírají v nejedenom případu i 80% obsahu celé stránky a hodně tak zvedají nepřehlednost v topiku. Chtěl bych všechny uživatele požádat, aby při postování citací, jakýchkoliv kódů, errorů, atp... delších než 30 řádků používali funkci spoileru.   Funkci vyvoláte příkazem [spoiler] text [/spoiler]   Ukázka:  
Sign in to follow this  
jolymp

baltie

Recommended Posts

hoj

dostal jsem do rukou zajimavy program na vyuku c# je pravda ze me osobne se moc nelibi ale pouziva se i na strednich skolach! a myslim ze se hodi krome deti i pro uplne zacatecniky aby si aspon okoukali strukturu programu a nazorne ukazali jak to vlastne program vznika vznika!

tady je strucny prehled a muzete zde nakouknout

(mozna to vypada stupidne ale ma to mnoho moznosti,vic nez se zda a zajimava je moznost vytvareni modelu v blenderu)

 

http://www.sgp.cz/cz/produkt-b4/B4_short_desc.htm

 

jestlize vas to aspon trochu zaujalo(neodsudte to hned) tak si do google zadejte baltie a najdete spoustu odkazu!

jestlize mate nekdo zajem (nebo to chcete vyzkouset jak to funguje a ne jen chodit s postavickou) tak pisnete mam na to hodne slusne lekce urcene strednim skolam ale velice srozumitelne a rad je uploadnu

 

download

http://www.instaluj.cz/cz/katalog/vyvojars...i/baltie-4-net/

 

B)

Share this post


Link to post
Share on other sites

Pracoval jsem s Balitíkem 2, ale pravda bylo mi 8let...

:P:D

v zavorce je pro mladsi :innocent: navic v 8 letech zna plno lidi na pocitaci jen huga a jeho hugolinu(ne ze by to nebyla dobra hra) tohle ze celkem zajimavy projekt...lidi(deti :P ) ci muzou vzit za priklad tebe ze si zacal na baltikovy a ted toho umis hodne :nw2_guitar:

Share this post


Link to post
Share on other sites

hh taky sem v tom s kamosem delal na zakladce v nejaky 7 tride... docela nam to slo byli sme 7 v celostatni soutezi :P

Ale je to pravda je to pro mladsi

Share this post


Link to post
Share on other sites

ja myslim ze je jedno kolik vam je kdyz se to chcete naucit :mid_pickaxe: pokud mate taky nekdo jiny nejake tutorialy na c# tak pisnete na vzse uvedeny mail nebo sem na tuto diskuzi

Share this post


Link to post
Share on other sites

U nás na střední vyrukovala před pár lety místní firma s výkovým softwarem pojmenovaným Petr http://www.gemtree.cz/peter.htm

Taky docela zajímavý barevný programovací jazyk určitě inspirovaný Baltíkem. Sice to bylo dělané tak nějak pro děti, ale věřím, že i klasické jazyky se snad někdy odpoutají od textových souborů a příjdou s něčím sofistikovanějším. Nakonec mnoho kódu se dnes už generuje automaticky při grafické editaci formuláře nebo třeba při modelování v UML.

 

Před časem jsem narazil na jeden zajímavý zahraniční projekt výuky programování pro děti http://scratch.mit.edu/

 

Share this post


Link to post
Share on other sites

Taky jsem v Baltíkovi dělal na základce (myslím že to byly verze 2, 3 a taky sme měli Baltazara 5). Je to spíš pro mladší.

Tu novou verzi jsem nezkoušel ale co vím tak by tam měl jít ručně dopisovat C# kód, takže by to mohlo být zajímavější i pro vyšší ročníky...

Share this post


Link to post
Share on other sites

me osobne to nelaka, z duvodu takovyho ze ta grafika v tom no bleee. Kdybych to mel porovnat s vytvarenim stranek. Tak to vypada jak Frontpage, mackas a leze ti kod a hov*o diky tomu umis, nez kdyby si to zkusil postupem casu delat sam normalnim zpusobem tj psani src kodu

Edited by Clant

Share this post


Link to post
Share on other sites

Jo, jenže je to určený především pro mladší ročníky.

Když ti bylo 10 - 11 - 12 let, tak jsi nepřemýšlel jako teď, hlavně jsi neměl znalosti jako teď ;)

Share this post


Link to post
Share on other sites
Jo, jenže je to určený především pro mladší ročníky.

Když ti bylo 10 - 11 - 12 let, tak jsi nepřemýšlel jako teď, hlavně jsi neměl znalosti jako teď ;)

 

 

mozna ze to je pro deti ale me to nejde ani nainstalovat :(:D

Share this post


Link to post
Share on other sites
hoj

dostal jsem do rukou zajimavy program na vyuku c# je pravda ze me osobne se moc nelibi ale pouziva se i na strednich skolach! a myslim ze se hodi krome deti i pro uplne zacatecniky aby si aspon okoukali strukturu programu a nazorne ukazali jak to vlastne program vznika vznika!

tady je strucny prehled a muzete zde nakouknout

(mozna to vypada stupidne ale ma to mnoho moznosti,vic nez se zda a zajimava je moznost vytvareni modelu v blenderu)

 

http://www.sgp.cz/cz/produkt-b4/B4_short_desc.htm

 

jestlize vas to aspon trochu zaujalo(neodsudte to hned) tak si do google zadejte baltie a najdete spoustu odkazu!

jestlize mate nekdo zajem (nebo to chcete vyzkouset jak to funguje a ne jen chodit s postavickou) tak pisnete mam na to hodne slusne lekce urcene strednim skolam ale velice srozumitelne a rad je uploadnu

 

download

http://www.instaluj.cz/cz/katalog/vyvojars...i/baltie-4-net/

 

B)

Vzdi ked to dam stahovat z neakeho serveru KRITIC ERROR :(

 

Share this post


Link to post
Share on other sites
CESTA K C++


Verze ze dne: 2.9.1998
Díly: 1 - 6
Historie C++
Jazyk C vznikl roku 1972 u firmy AT&T pod taktovkou Dennise Ritchieho. Jazyk byl vyvinut původně pro Unix (je zajímavé, že vlastně Unix v něm byl i napsán). V roce 1978 přišla další verze, tentokrát s názvem Kernighan-Ritchie C. První norma vznikla v r. 1984 - ANSI C, další pak roku 1990. Jazyk C++ je jazyk vycházející z C, ale je doplněn o plno nových lepších prvků - tím je například možnost používat naplno objektovou technologii programování (ta v C šla taky, ale bylo to hodně slabý). Nyní se již nové normy ANSI C neobjevují, zlepšují se totiž normy ANSI C++, který dnes již úplně nahradil starý C (vše co jde v C, jde i v C++ a leckdy daleko rychleji). Z hlediska překladačů mám nejraději Borlandské, protože mají nejsnáze ovladatelné prostředí, dobré "urychlovací" nástroje, nové verze splňují mezi prvními nové verze norem a firma má dobrou podporu. Při výkladu budu vycházet tedy z Borlandského C++, ale v 99% případů bude jedno, který používáte.
Program v jazyce C++ se ukládá do souborů *.C nebo *.CPP. Do těchto základních souborů se mohou vkládat hlavičkové soubory *.h. Ostatní soubory se tak často nepoužívají, nebo jsou používány u složitějších programů. Pro jednoduché programy pro DOS stačí otevřít nový soubor a začít psát program. Programy pro Windows mívají již složitější strukturu, takže jsou vytvářeny pomocí tzv. Project souborů - přípona PRJ, u novějších překladačů IDE (možno také vytvářet Makesoubory, které, pokud vím, využívají Microsoftí překladače). Tyto soubory jsou vytvářeny překladačem na základě vašich voleb. Obsahují jména souborů, které se mají slinkovat do EXE souboru a podobně. Ze začátku budeme vycházet jenom z té jednodušší varianty, tedy ze samostatných CPP souborů.
Struktura programu
Jazyk C++ je daleko volnější než třeba Pascal, co se týče struktury souboru. V Pascalu je soubor rozdělen do několika úseků (hlavička programu, deklarace typů, konstant, proměnných atd. ). V C++ tomu tak není. V podstatě je jedno, kde deklarujete proměnnou nebo nový typ. Její platnost začíná od místa deklarace a končí koncem bloku, v němž je deklarovaná. Naopak si musíte dávat pozor na psaní - C překladač rozeznává velká a malá písmena, to znamená, že literály "Pepa" a "PEPA" nejsou stejné. V tomto dělají Pascalisti rádi chyby.
V C++ jsou rozeznávány následující základní termíny:
• Klíčová slova
• Konstanty
• Operátory
• Identifikátory
• Návěští
• Komentáře
Klíčová slova jsou jednoduše řečeno příkazy jazyka, které jsou přesně dány, nedají se předefinovat.
Konstanty se dělí na numerické, znakové a textové (literály), enumerické, ... Je to obdoba proměnných, které mají ovšem po zadefinování neměnnou hodnotu.
Příkladem operátorů je například operátor pro sčítání (+).
U identifikátorů musí být první písmeno, další znaky mohou být čísla nebo písmena. Identifikují jména různých proměnných, typů a funkcí.
Návěští je určitý text v programu, na který je "odskakováno" po zavolání příkazu skoku (goto).
Do komentářů můžete vložit jakoukoli vaši poznámku k programu. Komentář je uvozen buď znaky // (dvě lomítka) nebo /* (lomítko a hned za ním hvězdička). Komentář uvozený // má platnost od uvedení lomítek až do konce řádku, komentář /* má platnost od tohoto začátku až do místa, kde jsou znaky */ (hvězdička a lomítko), které identifikují konec komentáře. Textu v komentářích si překladač vůbec nevšímá.
Příkazy se sdružují do bloků
{ (začátek - Pascalský begin)
} (konec - Pascalský end)
Příklad:
{ // Začátek nějakého bloku
příkaz1;
příkaz2;
...
příkazn;
} // Konec nějakého bloku
Jak jste si mohli všimnout, každý příkaz je ukončen středníkem. Na jednom řádku může být za sebou i několik příkazů, všechny však musí být odděleny středníkem (příkaz1; příkaz2; příkaz3).
Deklarace funkce
V C++ můžete (spíše musíte, jinak byste se v programu leckdy nevyznali) používat funkce. Procedury (z Pascalu) v C++ neexistují. Máte zde pouze funkce. Obecná deklarace funkce je následující
<typ> <jméno funkce> ( [parametry funkce] )
Typ udává, jaký typ dat se bude vracet, uveden musí být vždy. Jméno funkce udává jméno funkce, kterým ji budeme dále v programu volat. Za jménem jsou vždy závorky. Do nich se mohou vložit parametry. Parametry mají předem stanovený typ (ten se stanový při deklaraci), při volání musí být do závorek uvedeny proměnné nebo přímo hodnoty odpovídající jednotlivým typům. Tyto hodnoty za určitých speciálních podmínek nemusí být při volání uváděny, ale k tomu se dostaneme později. Pokud chcete zadefinovat funkce, která nebude vracet žádnou hodnotu, jako typ použijte klíčové slovo void.
Taková deklarace vypadá třeba takto:
void Funkce1( int Parametr1, int Parametr2 )
{

}
Takto jsme si zadefinovali funkci Funkce1, která nevrací žádnou hodnotu a jako parametry má dvě proměnné typu int (tento typ je obdobou Pascalského integeru, tedy 2-bytového čísla). Tato funkce ale nic nevykoná, při překladu by vás měl překladač varovat, že proměnné Parametr1 a Parametr2 nejsou využity. Tato funkce je klasickým příkladem na mrtví úsek programu.
Stejně tak bychom si mohli zadeklarovat funkci Funkce2, která bude vracet hodnotu typu int a bude bez parametrů:
int Funkce2() {
int VracenaHodnota;

return VracenaHodnota;
}
Tato funkce také ještě nic neudělá. V těle funkce je deklarována proměnná VracenaHodnota typu int a na konci funkce je klíčové slovo (return), které říká, že tělo funkce končí a co se má vrátit. V našem případě se bude vracet hodnota v proměnné VracenaHodnota. Klíčové slovo return musí být uvedeno u každé funkce, která nemá návratový typ void. Void-funkce nesmí obsahovat return.
Kvůli přehlednosti se někdy zadeklaruje pouze na příslušném místě prototyp funkce a někde jinde, kde se to hodí, se napíše celá deklarace. Tak například kdybychom to chtěli udělat u funkce Funkce2, vypadalo by to takto:
int Funkce2(); /* Toto je prototyp, takže zde musí být středník. Výsledně se chová, jakoby deklarace byla tady */
int Funkce2() /* Zde již středník není, tato deklarace může být uvedena, ke se vám to hodí */
{
int VracenaHodnota;
return VracenaHodnota;
}
Pokud máte funkci s parametry, stačí do závorek napsat pouze jejich typ, názvy se psát nemusí (když je napíšete, nic se nestane).
Výpis na obrazovku
Nyní je na čase, abychom si ukázali, jak se dá vypsat jednoduchý text na obrazovku. Ještě malou poznámku - ze začátku se budeme zabývat programováním pro DOS, pro Windows se výstup na obrazovku nebo základní programový blok deklaruje trochu jinak.
Pokud chcete přiložit k programu nějaký hlavičkový soubor, uděláte to pomocí direktivy
#include <jméno souboru>
Znaménka < a > mohou být nahrazena uvozovkami. Pomocí těchto souborů se například rozšiřuje příkazová škála jazyka, avšak využití těchto souboru je mnohem širší. Funkce, které budeme používat pro výstup na obrazovku jsou definovány v souboru stdio.h, napíšeme tedy na začátek souboru *.CPP (ten si pojmenujte jak chcete, v příloze je tento program přepsán a má název cpp01.cpp):
#include <stdio.h>
Tento řádek lze uvést kdekoli v programu (vždy však před prvním použití funkcí nebo typů definovaných v .H souboru), ale pro přehlednost je zvykem dávat direktivy #include na začátek souboru.
Základní programový blok, který se spustí hned po spuštění programu (samozřejmě po provedení nezbytných úvodních úkolů) je funkce main, která má libovolný návratový typ a libovolné parametry. Je ovšem zvykem dávat návratový typ jako void. Parametry main funkce jsou parametry, které se zadávají při spouštění programu z příkazové řádky za EXE soubor. Program pro výpis pozdravu by mohl vypadat třeba takto:
#include <stdio.h>
void main()
{
printf( "Ahoj\n" );
printf( "Dnes máme ale skvělé počasí" );
}
Funkce printf je definována v souboru stdio.h . Umožňuje výpis formátovaného textu na obrazovku. Kdybychom chtěli na monitor dostat hodnotu proměnné x, můžeme to udělat takto:
printf( "\nHodnota proměnné x je: %d", x );
Znak uvedený za jedním zpětným lomítkem (\) udává nějakou činnost, která se má provést. Například písmeno n indikuje, že se má odřádkovat (enter). Pokud chcete k zobrazovanému textu připojit nějakou proměnnou, zadejte na místo, kde má být, znak procenta a za něj písmeno indikující daný typ (pro typ int je to znak d). Jakmile ukončíte literál k vytisknutí pomocí uvozovek, napište čárku a za ní v pořadí, v jakém jste proměnné v literálu uváděli, jména proměnný, jejichž hodnoty se mají za procento+znak dosadit (v našem příkladu se místo %d vypíše hodnota proměnné x. Pro ty, kteří přecházejí z Pascalu se to bude asi zdát strašně nepřehledné a nepohodlné, ale věřte mi, že si na to brzy zvyknete a naučíte se to rychle číst. Takováto syntaxe totiž šetří hodně místa v programu.
Nyní uvádím nejpoužívanější hodnoty uváděné za "procento" a zpětné lomítko:
Sekvence: Hex. hodnota: Význam:
\n 0x0A nová řádka
\r 0x0D návrat na začátek řádky
\f 0x0C nová stránka
\t 0x09 tabulátor
\b 0x08 posun doleva (backspace)
\a 0x07 písknutí
\\ 0x5C 1 zpětné lomítko
\´ 0x2C apostrof
0x00 nulový znak
\" uvozovky
Sekvence: Typ:
%c znak
%d signed int
%d signed long
%u unsigned int
%lu unsigned long
%f float - může být i double
%Lf long double
%lf double
%x hexdec.číslo malými písmeny (2b3a)
%X hexdec. číslo velkými písmeny (2B3A)
%o osmičkové číslo
%s řetězec
Základní typy proměnných
Zde si uvedeme nejzákladnější používané typy v C

Typ Délka Rozmezí
unsigned char 8 bitů 0 až 255
char 8 bitů -127 až 127
enum 16 bitů -32.768 až 32.767
unsigned int 16 bitů 0 až 65.535
short int 16 bitů -32.768 až 32.767
int 16 bitů -32.768 až 32.767
unsigned long 32 bitů 0 až 4.294.967.295
long 32 bitů -2.147.483.648 až 2.147.483.647
float 32 bitů 3,4 * 10-38 až 3,4 * 10+38
double 64 bitů 1,7 * 10-308 až 1,7 * 10+308
long double 80 bitů 3,4 * 10-4932 až 3,4 * 10+4932

Tyto typy platí pro 16-bitové překladače. 32-bitové mají tyto a některé další, ale ty nejsou tak důležité. Pascalisti pozor! - v Pascalu je jeden standardní typ integer, který slučuje 3 Céčkovské - int, long int, short int.
Typ enum, někdy nazývaný jako výčtový, částečně nahrazuje Pascalské set. Pomocí enum si můžete definovat nové typy, které budou vycházet standardně z int, ale mají omezenou množinu hodnot, jichž mohou nabývat. Takto si můžeme například zadefinovat logický typ boolean, který není standardně Céčkem podporován. Jeho deklarace je jednoduchá:
enum boolean {false, true};
Za jméno nového typu (boolean) se uvádí složené závorky, do nichž se vpisují hodnoty, kterých může typ nabívat. Ve své podstatě je enum typem číselným, to znamená, že každá hodnota představuje určité číslo (false je 0, true je 1). Zde jsem číselné hodnoty uvádět nemuseli, překladač to za nás udělá sám, začne nulou a pak pokračuje po jedné výše. Pokud byste chtěli začít jiným číslem, stačí napsat třeba false=5, true . Hodnotě true již nemusíte zadávat hodnotu 6, ale když ji napíšete, nic se nestane, jen vám to usnadní orientaci.
Definice proměnných
Obecně se to dá napsat takto:
typ jméno [=hodnota], ... ;
U deklarace musí být typ uveden vždy a jméno také. Volitelně při deklaraci můžete uvést i počáteční hodnotu proměnné. Po dokončení deklarace můžete napsat čárku a za ni jméno další proměnné, která bude téhož typu jako předchozí. Pokud máte nadeklarovány všechny proměnné, které na jednom řádku chcete mít, udělejte středník.
Příklad:
int Proměnná1, Proměnná2=5;
char Znak;
int Proměnná3;
Z tohoto příkladu je patrné, že deklaraci proměnných téhož typu můžete rozložit do několika řádků. To by bylo pro dnešek asi vše. Příště si povíme něco o operacích s proměnnými a jiných dalších základech.
Konstanty
Konstanty v C můžete definovat dvěma způsoby:
• pomocí klíčového slova const
• pomocí direktivy #define
Pomocí const je pouze udáno, že nějaké "proměnné" po jejím zadefinování se nesmí měnit hodnota. Používání tohoto klíčového slova není tak časté, jako #define.
Příklady:
const int i = 5;
#define i 5
Použití const je myslím díky příkladu jasné. Za direktivou #define se uvádí konstanta, kterou chceme zadefinovat a za ni (odděleno mezerami nebo tabulátorem) její hodnota. S typem se zde nepracuje, protože všude tam, kde takto definovanou konstantu použijete, se vloží její hodnota. Tyto konstanty jsou v C++ velmi často používány. Pro zatvrzelé Pascalisty může být užitečné toto:
#define begin {
#define end }
Když tuto definici uvedete na začátku programu, budete moci potom používat všude v programu vaše oblíbené begin a end, aniž byste se museli bát, že si to spletete s poznámkou.
Operace proměnnými
Základní operace jsou totožné jako u ostatních jazyků (sčítání, odečítání, násobení, dělení atd.). Oproti Pascalu zde jsou jen menší rozdíly. U přiřazování k proměnné p se místo := používá pouze = (jedno rovnítko). K porovnávání se nepoužívá jedno rovnítko, ale 2 uvedené hned za sebou (x==y).
Příklad:
p = 10;
p = p +1;
Pokud přiřazujete znak (ne řetězec), vkládá se do apostrofů (char c = ´x´). Přiřazování může být i několikanásobné:
a = b = c = d = 3;
V tomto případě mají proměnné a, b, c, d stejnou hodnotu (3). Pro přičtení jedničky k proměnné se používají dva plusy:
p++;
// tento výraz je totožný se zápisem p = p +1;
To samé platí i pro odečítání jedničky:
p--;
// totožné s p = p -1;
Tyto výrazy se dají používat jako prefix nebo suffix (p++ nebo ++p). Rozdíl je patrný, pokud tyto operátory zamícháte do složitějšího výrazu.
p = 12 + b + c++;
// do p se dosadí součet 12 + b +c. Nakonec se c zvětší o jedničku

p = 12 + b + ++c;
// c se změní o jedničku, do p se dosadí součet 12 + b + (c+1)
Pokud přičítáte k p nějakou hodnotu nebo výraz a výsledek ukládáte do p, dá se to napsat takto:
p+=12;
// totožné s p=p+12;

p+=a+b*c-50;
// totožné s p= p + (a+b*c-50). Závorky uvádím pro názornost
Na tomto místě by bylo možná vhodné si zavést další pojem, je to l-hodnota. L-hodnota je adresa (např. proměnná p), ovšem konstanty (12) nebo výrazy (a+12) l-hodnotami nejsou. L-hodnota je tedy to, co můžeme dát na levou stranu přiřazení.
Kromě výše uvedeného přiřazovacích operátoru (+=), C zná i tyto: -=, *=, /=, %=, >>=, <<=, &=, ^=, |=.
Význam binárních operátorů:
Funkce
Pascalský operátor C operátor
sčítání + +
odečítání - -
násobení * *
reálné dělení / /
celočíselné dělení DIV /
dělení modulo MOD %

V C operátory >> a << slouží k bitovému posunu (doprava a doleva). Operace p << 2 posune bity v p o 2 místa doleva, původní první dva bity se vymažou a z druhého konce se nahradí 0. To samé platí i pro >> (ale obráceně, posouvá se doprava).
Rozdělování celočíselného a reálného dělení je prováděno automaticky (rozhodování o tom, jaký typ dělení to má být je logické a není potřeba ho nějak zvlášť definovat):
int / int
celočíselné
int / float reálné
float / int reálné
float / float reálné
Protože double a long double jsou desetinná (stejně jako float), uváděl jsem pouze float, ve skutečnosti platí to samé i pro ně.
Vstupy
Nejprve se budu zabývat formátovaným vstupem - funkce scanf(). V podstatě je to takový protiklad k funkci printf(). Je také definována v stdio.h. Platí pro ni podobné podmínky jako pro funkce printf(). Její syntaxe je:
scanf( formát, parametry );
Za formát se dosazuje textový řetězec, který specifikuje určitou vkládanou hodnotu. Pro vstup čísla int by to mohlo vypadat takto: scanf( "%d", &p); - p musí být proměnná typu int
Před jméno proměnné se dává znak &, který zapříčiní, že je předávána adresa proměnné, kde je v paměti uložena (ukazatel - viz můj článek v některém předcházejícím čísle, možná se k nim časem ještě vrátím).
Příklad na tuto funkci najdete v souboru cpp02_01.cpp.
Dalším příkazem pro vstup je příkaz getchar(), který přečte pouze jeden znak. Je bez parametrů, načtený znak dává přes návratovou hodnotu. Pozor - tato funkce nemá návratovou hodnotu typu char, nýbrž int! Ve své podstatě je znak char numerický, tedy není textový. Veškeré operace s tímto typem se dějí na základě matematických operací s čísly (numerickými hodnotami). Po skončení operací je číslo (0-255) převedeno podle právě aktivní ASCII tabulky na znakovou hodnotu. Typ char je tedy celočíselný jednobytový (=> rozmezí 0-255). Příkaz getchar() má návratovou hodnotu typu int kvůli tomu, že se také používá u operací se soubory (k tomu ale až potom). Pokud ovšem od uživatele čekáte znak (max. hodnota 255), stačí když si zadefinujete proměnnou "znak" typu char. C++ se totiž vždy snaží zkonvertovat nějaký typ na jiný, když je to potřeba. Někdy, když to překladač neudělá automaticky za nás, je potřeba napsat před proměnnou konvertovaného typu do závorek typ, na který budeme převádět. Takže definice
char znak;
int cislo;

cislo = 5;
znak = cislo; /* zde se nemusí uvádět, na co budeme konvertovat, ale definice znak = (char) cislo by byla přehlednější */
zapíše do proměnné typu char hodnotu 5, kterou přebrala z proměnné typu int. V 95% případů se konvertovat dá, vždy to nějak dopadne, někdy úspěšně, někdy ne. Kdybychom do cislo vložili 300 hodnota je mimo rozsah typu char), do znak se uloží 44. Pokud je totiž konvertovaná hodnota mimo rozsah, "vymaže se" začátek, a vloží se jen to, co se do proměnné vejde (zde se počítalo: 300 - počet prvků v char = 44).
Z toho platí, že když napíšete
char znak;
znak = getchar();
a vstupní zařízení bude klávesnice, bude vše Ok. Pokud by byl vstup ze souboru a hodnota by byla větší než 255, provede se konverze. Jak tedy budete tuto funkci používat, nechám na Vás. Jako scanf() má svůj opak ve funkci printf(), funkce getchar() má opačnou funkci putchar(). Tato funkce vyšle jeden znak na výstupní zařízení, tento znak (hodnotu) dostává parametrem (int putchar(int znak) ). Pokud funkce proběhne v pořádku, vrátí se hodnota "znak", jinak se vrátí hodnota EOF (end of file, k tomu až někdy později. EOF je konstanta definovaná v stdio.h). Pokud chcete na výstupní zařízení poslat znak (ne číslo), vložte ho do apostrofů ('p'). Pozor - literály (řetězce) se uvádějí v uvozovkách, samostatné znaky v apostrofech.
Deklarace nového typu
Nový typ se v C++ dá zadefinovat pomocí klíčového slova typedef. Jeho syntaxe je:
typedef starý nový;
Z této syntaxe plyne, že deklarace nového typu je vlastně pouze přejmenování starého. Má to význam např. u typů s dlouhým jménem (unsigned long int a pod.). Deklarace tohoto typu by mohla vypadat třeba takto:
typedef unsigned long int NovyTyp;
Tímto způsobem zkrátíme dlouhý zápis na malý NovyTyp. V programu pak stačí napsat
NovyTyp NovaPromenna;
a proměnná NovaPromenna bude mít stejné vlastnosti jako jiná, která je typu unsigned long int. Možná si říkáte, že je to k ničemu, ale občas se to hodí (třeba u struktur, ale tam je to spíše pro přehlednost).
Řízení běhu programu
Úplné základy jsou za námi. Teď se budeme věnovat již zajímavějším záležitostem programování. Stejně jako ve většině jiných programovacích jazycích, tak i v C nemusí program jít "monotónně" za sebou, ale je možné ho určitým způsobem řídit. K tomu slouží plno druhů podmínek, smyček a skoků.
Podmínky
Podmínky umožňují provést určitý příkaz (množinu příkazů) pouze za určitých podmínek. Asi nejvíce používanou je podmínka if. Stejně jako v Pascalu se její provádění dělí na dvě části - první, pokud podmínka je true a na druhou, uvozenou klíčovým slovem else, pokud je podmínka rovna false. V C neexistuje něco jako then (z Pascalu či Basicu), ale za podmínkou (uvedenou v závorkách) je rovnou uveden příkaz, popřípadě blok příkazů. Po skončení bloku se normálně uvede else a pokračuje se dalším příkazem nebo blokem. Pokud za if uvedete pouze 1 příkaz, je ukončen středníkem (neplést s Pascalem!). Slovo else se, pokud není potřeba, uvádět nemusí.
Příklad:
int x=5;

if (x==10) printf( "To je ale blbost");
else printf("To je ono!");
Už jsem se o tom zmiňoval, ale pro přiřazení se používá jedno rovnítko (=) a pro porovnání dvě za sebou (==). Pokud by jste chtěli vyjádřit opak (jestliže se x nerovná 10), použijte operátor !=. Vykřičník by se v C dal charakterizovat jako operátor negace. Několik podmínek můžete mísit v jednu, třeba "jestliže x=10 a y=20" se napíše:
if (x==10 && y==20)
Jednotlivé "podpodmínky" se nemusí závorkovat pokud k tomu není logistický důvod. Operátor && je operátor odpovídající Pascalskému AND. Operátor || odpovídá OR.
Kromě podmíněného příkazu if existuje v C také tzv. podmíněný výraz, který Pascal nemá. Je to obdoba příkazu if, ale jsou použity speciální znaky. Syntaxe je:
podmínka ? výraz1 : výraz2;
kde podmínka je podmínka, která, je-li rovna true, vyvolá provedení výrazu1. Pokud je nepravdivá, provede se výraz2. Odpovídá to:
if (podmínka) výraz1;
else výraz2;

Použití je vhodné například v přiřazování:

int a, b, c;
b=2;
c=5;
a = (b==2) ? 10 : 20; // v tomto případě totéž co a=10
Jak vidíte, velmi se zkrátí zdrojový kód, ovšem pro méně zkušeného "čtenáře instrukcí" je to velmi nepřehledné, proto se častěji používá if. Náš příklad by pak vypadal:
int a,b,c;
b=2;
c=5;
if (b==2) a = 10;
else a = 20;
Cykly
Co je to cyklus asi víte, takže se tím nebudu zabývat. K implementaci cyklů do C-programu můžete použít 3 základních způsobů (záleží na tom, co považujete za příkaz skoku a co za cyklus).
Nejjednodušším je while. Z jeho syntaxe
while (podmínka) příkaz
plyne, že podmínku, za které se příkaz (blok příkazů), který následuje za závorkou, provede, je v závorkách. Tato podmínka může být i složitější a pro dané podpodmínky platí to samé jako pro if. Podmínka se testuje při vstupu do cyklu a následně po vykonání příkazu - pokud je true, jede se s prováděním příkazů znovu. Z toho plyne, že se tělo cyklu nemusí provést ani jednou (pokud podmínka je hned na začátku pravdivá). Použití tohoto cyklu je v nepřeberném množství příkazů, nejjednodušším je asi čekání na stisk požadované klávesy. Příklad je v cpp02_02.cpp.
Dalším je cyklus do-while. Je to obdoba while, ale testování podmínky se provádí až na konci, takže příkaz (blok příkazů) v těle cyklu se vždy provede alespoň jednou. Syntaxe je:
do
prikaz; // může být blok příkazů ve složených závorkách
while (podmínka) ;
Cyklus je uvozen samostatným slovem do, za ním následuje příkaz a nakonec klíčové slovo while s podmínkou v závorkách. Na konci řádku je středník. Příklad je v cpp02_02.cpp.
Zřejmě nějpoužívanějším příkazem cyklu je for. Jeho syntaxe je:
for (příkaz00;podmínka;příkaz01)
příkaz1; // popřípadě blok
Za uvedením klíčového slova for jsou v závorce uvedeny 3 "parametry" (mám to v uvozovkách, protože za parametry v plném slovasmyslu se to považovat nedá). Příkaz00 je vykonáván vždy, než se vstoupí do cyklu. Za celou dobu trvání (i když se opakuje 5x) je prováděn jen jedenkrát. Podmínka indikuje, zda se má cyklus opakovat nebo ne. Příkaz01 je prováděn vždy na začátku dalšího provádění (pokud se příkaz1 provádí 5x, příkaz01 se provede také 5x). Příkaz1 může být nahrazen blokem příkazů. Příkaz00 obvykle obsahuje počáteční inicializaci čítače a příkaz01 operaci s ním.
Příklad:
int i;
for (i=0;i<10;i++)
printf( "Hodnota čítače: %d", i );
V tomto příkladu se vypíše 10 zpráv. První bude "Hodnota čítače: 0". Hodnota bude stoupat, až se dostane na 9, kde cyklus skončí. Hodnota 10 se již nevypíše, protože po skončení 10. cyklu (i==9) se přičte do i jednička (i==10). To již nesplňuje podmínku i<10, protože i==10. Kvůli tomu se cyklus ukončí. Pokud byste chtěli i zvětšovat po 5, místo i++ napište i = i +5. Pokud byste chtěli používat proměnnou i pouze v těle cyklu a nikde jinde, můžete napsat místo i=0 int i=0 a nad smyčku nepsat int i. Tímto se zadefinuje proměnná i pouze pro tento cyklus. Po skončení cyklu bude volání proměnné i považováno za neplatné (samozřejmě pokud není zadefinována ještě někdy jinde). Tato syntaxe je podporována až novějšími verzemi ANSI C++ (pokud vím, v C to nešlo). V BC++ 5.0 a vyšších je platnost takto deklarované proměnné také za tělem cyklu až do skončení aktuálního bloku (zda to přikazuje nejnovější verze ANSI C++, nebo je to nepovinné vylepšení překladače nevím, teď se mi to plete).
Skoky
Mezi příkazy skoků patří klíčové slovo break. To umožňuje přeskočení na konec aktuálního bloku a tím vynechání všech příkazů od aktuálního místa až do konce bloku.
Příkaz goto. Tento příkaz umožňuje skok z nějakého místa na místo jiné. Je to ovšem omezeno pouze na blok jedné funkce (je zakázáno skákat z funkce A do funkce B pomocí tohoto příkazu). Za příkaz se uvádí název tzv. návěští. Návěští je deklarováno někde jinde v programu. Ze syntaxe příkazu
navesti:
... /* programový kód */
goto navesti;
plyne, že se skočí na místo, které je uvozeno příslušným návěštím navesti. Na rozdíl od Pascalu se nemusí deklarovat na začátku (přes label, pokud si dobře vzpomínám). Stačí když na zvolené místo napíšete jméno návěští a za něj dvojtečku (:). Za příkaz goto pak napište jeho jméno a středník. Používání těchto skoků je proti logice jazyka C, proto by se měli používat jen v nevyhnutelném případě. Častější používání také zhoršuje optimalizaci kódu programu a zpomaluje běh aplikace. Dále používání tohoto příkazu velmi znepřehledňuje zdrojový kód. Když je ale budete používat, dávejte si pozor na správné určení místa "doskoku". Například, když budete skákat doprostřed nějakého bloku, obyčejně vynecháte místa pro inicializaci proměnných a může se stát, že nějaká proměnná nebude mít požadovanou hodnotu (v lepším případě) nebo nebude zadefinována vůbec (to už bude horší). Tu poslední chybu dokáže odhalit jen málo překladačů hned při kompilaci. Pokud přecházíte z Pascalu, kde používání tohoto příkazu bylo zcela běžné, pokuste se odnaučit ho používat, k C to moc nepatří a v 99,5% případů se tomu dá vyhnout pomocí jiné C syntaxe.
Příkaz switch umožňuje provedení určité množiny příkazů pouze v případě, že nějaká proměnná obsahuje určitou hodnotu. Syntaxe je:
switch (proměnná)
{
case hodnota1:
case hodnota2:
...
case hodnotaN:
default:
}
Proměnná je proměnná, která se bude porovnávat s určitými hodnotami, které se uvádějí za klíčová slova case. Za hodnotu se dává dvojtečka, za kterou se píše blok příkazů (který nemusí být oddělen složenými závorkami, takže blok se tomu vlastně ani říkat nedá), který obvykle končí příkazem break. Pokud byste break neuvedli, pokračovalo by se v provádění příkazů u dalšího case-bloku (popř. default), dokud se nenarazí na nějakém break. Za default se nedává žádná hodnota, tato část se provádí, pokud proměnná není shodná s žádnou hodnotou uvedenou za case. Příklad najdete v souboru cpp02_02.cpp.
Pole
Obecná deklarace pro pole je asi tato:
[pam. třída] typ jméno[počet prvků]
V prvním díle seriálu jsem uváděl, že proměnná se deklaruje způsobem:
typ proměnná
Ve skutečnosti je deklarace podobná s deklarací polí, tedy alespoň na začátku. Správná obecná deklarace je taková:
[pam. třída] typ jméno
Stejně jako u polí se paměťová třída udávat nemusí, automaticky je pak brána jako auto. Tato třída je používána u lokálních proměnných(proměnných, které jsou platné pouze v aktuálním bloku a jeho podblocích; mimo je neplatná).
Další paměťovou třídou je třída register. Tato třída má opět smysl pouze u lokálních proměnných. Udává, že nějaká proměnná má být uložena do registru procesoru místo do paměti. Přesné umístění a to, zda ji vůbec do registru uložit jde, si zvolí překladač sám. Na takto deklarovanou proměnnou nelze vytvořit ukazatel. Výhoda je jasná - přístup k hodnotě je v podstatě v čase s nulovou prodlevou. od zažádání procesoru.
Třída static má určité vlastnosti globálních proměnných (proměnných s platností ve všech blocích programu), tedy při znovuvstoupení do určitého bloku programu, kde byla deklarována, má stejnou hodnotu, jako když blok opouštěla. Rozdíl mezi static a globální proměnnou je v tom, že se na ni můžeme odkazovat pouze v bloku, kde je deklarována, přestože je po deklaraci až do konce programu stále v paměti.
Třída extern vytváří odkaz na již existující globální proměnnou. Praktické využití je ve vícemodulovém programu (programu, který se skládá z více než 1 programového souboru - u BC++ je to nutné vytvořit přes projekt). Máte-li projekt skládající se ze 2 modulů (A.cpp a B.cpp) a v modulu A.cpp si zadeklarujete proměnnou i typu int, se kterou chvilku pracujete (změní se její hodnota, aby tento příklad měl praktický význam), a potom se volá nějaká funkce deklarovaná v modulu B.cpp, která bude potřebovat hodnotu v i, ale z nějakého důvodu jí tuto hodnotu nebudete moci předat přes parametr, zadeklarujete si v modulu B.cpp proměnnou i typu int a s paměťovou třídou extern. Potom s touto proměnnou již budete moci bez problémů pracovat.
Nyní zpět k polím. Pole má stejnou deklaraci jako proměnná, ale za jménem se udává do hranatých závorek ( [ ] ) velikost jednoho rozměru pole. Z toho plyne, že lze vytvořit i vícerozměrná pole. Velikost každého rozměru se potom vkládá do vlastních hranatých závorek. Deklarace dvourozměrného pole s typy prvků int s velikostí 10x5 polí vypadá takto:
int Pole[10][5];
Přístup k jednotlivým prvkům je stejný jako v Pascalu - přes složené závorky (Pole[3][2]=15;). Pole se začíná zaplňovat od nuly a poslední prvek v každém sloupci nebo řádku programátor nepoužívá. Velikost pole, stejně jako velikost proměnné, nesmí přesáhnout velikost jednoho segmentu (u 16bitových překladačů to je 64kb u 32bitových to jsou 4Gb, což pro malé a střední programy je takřka neomezená hranice).
Stejně jako proměnné, i pole se mohou inicializovat hned při deklaraci:
int Pole1[5] = {12,0,5,4,3};
int Pole2[] = { 4, 3, 2 };
int Pole3[5] = { 5, 4, 3 };
U Pole1 je to asi jasné - do Pole1[0] se uloží 12 a tak to půjde dál až na konec pole. Pole2 se automaticky vytvoří na nejmenší možnou hodnotu - tedy na pole o třech využitelných prvcích (Pole[3]). Pole5 se inicializuje od nultého prvku (Pole3[0]) až po tolikátý, kolik máte zadáno hodnot. Zbytek prvků zůstane nezměňeno (tedy v nich bude taková hodnota, jaká je v paměťových buňkách již z dřívějška od jiné proměnné). Na to si dávejte pozor i u proměnných, doporučuji nespoléhat na to, že po deklaraci je hodnota 0, je to ve velmi malém množství případů.
Příklad na pole je v souboru cpp03_02.cpp.
Preprocessor
Preprocessor je část překladače, která zpracovává zdrojový kód ještě před vlastním překladačem. Tento kód připravuje pro překladač. Preprocessor si z programu "vybírá" pouze příkazy uvozené znakem #. Tento znak uvozuje tzv. direktivu (příkaz preprocessoru). Příkaz pro preprocessor je ukončen enterem, nikoli středníkem. Bez těchto direktiv se sice programátor obejde (program lze napsat i bez nich), ale velmi často se používají, zejména pro urychlení překladu, programového kódu, zamezení překladu na nevyhovujícím překladači atd. Preprocessor také vkládá jiné soubory do základního programového souboru, definuje různá makra, která také zpracovává.
Již dříve jsem uvedl, že direktiva #define je určena pro definici konstant. V podstatě slouží k zadefinování makra. Pokud byste chtěli v určitém místě programu zrušit platnost makra, použijte direktivu #undef.
Příklad:
#define Makro1 18+15
x=Makro1; // v pořádku
...
#undef Makro1
x=Makro1; // chyba, Makro1 je neznámý termín
Testování existence a obsahu makra
Využití otestování, zda je nějaké makro definováno, je rozsáhlé. Nejčastěji se však používá pro testování, zda je nějaký soubor již vložen (pomocí direktivy #include). Slouží k zamezení překladu nějaké části programu, pokud není potřeba.
Pokud chcete rozdělit překládaný text jen na dva bloky, slouží k tomu 3 příkazy: #if, #else, #endif.
#if podmínka
....
#else
....
#endif
Pokud je podmínka splněna, přeloží se blok mezi #if a #else, jinak se přeloží blok mezi #else a #endif.
Direktiva #else se uvádět nemusí (záleží na tom, zda je k tomu logický důvod).
Pokud je potřeba text rozdělit na více než dvě části, slouží k tomu příkazy #if, #elif, #endif.
#if podmínka
...
#elif podmínka
...
#elif podmínka
...
#endif
V podmínce je možno použít všech operátorů z C++ (==, !=, <=, ...) . Navíc je možno použít operátoru defined, který otestuje existenci nějakého makra. Jeho syntaxe je:
defined( makro )
Tento operátor se velmi často používá spolu s operátorem !:
!defined(makro)
Direktiva #include
Pomocí této direktivy vložíte do základního programového textu text z jiného souboru (nejčastěji z hlavičkového). Jméno vkládaného souboru se uvádí buď do uvozovek nebo do znamének větší menší (< >). Do uvozovek se nejčastěji vkládají soubory vytvořené programátorem, protože takto definovaný soubor je hledán i v aktuálním adresáři (soubor uvedený v < > se hledá pouze v zadefinovaných cestách - u BC++ 4,52 to je v Options|Projekt|Directories ).
Na tomto místě by také bylo vhodné se zmínit o takzvaných Falešných makrech. To jsou makra, která jsou sice zadefinována, ale nemají žádnou hodnotu.
Např.:
#define __math_h
U tohoto makra není definována hodnota. Dá se použít například pro otestování, zda nějaký hlavičkový soubor již byl vkládán nebo ne. U většiny hlavičkových souborů můžete nalézt toto:
#if !defined(__math_h)
#define __math_h
....
#endif /* end of file */
Zde jsem použil příklad ze souboru math.h. Než se soubor vloží do programu, otestuje se, zda neexistuje makro __math_h. Pokud ne, zadefinuje se (bez udané hodnoty) a dále se zpracuje kód mezi #if a #endif. Za #endif je již uveden znak pro konec souboru, tedy konec hlavičkového souboru.
Toho se dá využít při ochraně proti dvojnásobnému zavádění kódu, což je zbytečné.
Když potom někde použijete
#include <MATH.H>
#include <MATH.H>
soubor math.h se zavede pouze jednou. Možná se Vám to jeví jako zbytečné, že byste se takto nespletli, ale hodně hlavičkových souborů potřebuje pro provedení deklarace vnich uvedené nějaký jiný soubor. Když se náhodou takto sejdou dva hlavičkové soubory, které budou požadovat 1 soubor (např. matika.h a pocty.h požadují math.h), zavede se daný soubor pouze jednou.
Makra s parametry
Pokud si potřebujete vytvořit nějakou jednoduchou funkci, např. umocňování, je vhodné k tomu použít makro, protože vykonání makra je daleko rychlejší než volání funkce.
Pro umocňování by makro vypadalo takto:
#define Umocni(n) ((n)*(n))
Pokud byste někde použily x=Umocni(15), do x se dosadí druhá mocnina 15. Definice možná vypadá složitě, ale není. Za jménem makra se uvede v závorkách jméno výrazu (bez typu, jedná se přece o makro). Potom následuje hodnota makra, která se bude vkládat všude tam, kde bude makro voláno (pochopitelně naše n se změní na výraz). Hodnotu makra jsem hodně "zazávorkoval" kvůli tomu, kdyby někdo chtěl náhodou umocňovat záporné číslo. Parametrů makra může být více, záleží na tom, jaké makro zrovna chcete.
Direktiva #error
Kdykoli se narazí na tuto direktivu, vypíše se zpráva (ve zprávách -Message- po kompilaci) uvedená za direktivou. Z logického důvodu je nejčastěji používána společně s direktivou #if.
Příklad:
#if defined(__soubor_h)
#error Pokus o znovu zadefinovani souboru
#else
#define __soubor_h
#endif
Tento příklad by se málokdy použil, ale pro pochopení této direktivy je velmi dobrý.
Často se používá u 16-ti bitových překladačů při testování, pod jakým paměťovým modelem je program překládán.
Příklad:
#ifdef __SMALL__
#error Nelze prelozit pod pametovym modelem SMALL
Předdefinovaná makra
Na tomto místě uvádím seznam maker, která jsou implicitně zadefinována (překladačem):
Makro:
Význam:
__BORLANDC__ Číslo verze překladače (pouze u Borlandských překladačů). Verze 3,1 - hodnota 0x410, verze 4,0 a 4,02 - 0x452, verze 4,5 - 0x460
__TURBOC__ Číslo verze TC++ a BC++. Hodnoty jsou stejné jako u předchozího makra.
__cplusplus Pokud se překládá pod C++, má hodnotu 1, jinak nedefinováno.
__DATE__ Aktuální datum.
__TIME__ Aktuální čas.
__FILE__ Právě překládaný soubor - jméno.
__LINE__ Právě překládaná řádka.
__DLL__ Pokud se generuje kód do DLL souboru, má hodnotu 1, jinak nedefinováno.
__Windows Pokud se generuje kód pro Windows, má hodnotu 1, jinak nedefinováno.
__OVERLAY__ Pokud se generuje kód DOS aplikace s překryvnými segmenty, má hodnotu 1, jinak nedefinováno.
__WIN32__ Při 32bitovém překladu (GUI i CONSOLE) má hodnotu 1, jinak nedefinováno.
__TINY__ Paměťový model, do kterého je překládáno je Tiny (obdobně i níže).
__SMALL__
__MEDIUM__
__COMPACT__
__LARGE__
__HUGE__
Přetěžování funkcí
Preprocessor je za námi. Nyní bych se ještě rád vrátil k funkcím. V 1. Díle seriálu jsem se o nich trochu zmiňoval, nyní bych to chtěl ještě rozvést.
Přetěžování funkcí je velice užitečná věc, podporuje ji však jenom pár jazyků. Je to v podstatě možnost používat v programu několik funkcí stejného jména, ale s jinými typy parametrů. Překladač si potom automaticky zvolí tu správnou podle toho, jaký parametr zadáte. Příklad je v souboru cpp03_01.cpp.
Funkce definované v jiném modulu
U větších programů je nezbytné, aby se program skládal z více než jednoho souboru. Nevím, jak je to přesně řešeno u Microsoftích nebo Watcomských překladačů (zřejmě nějak přes MAKesoubory). U Borlandských překladačů jsou tyto soubory sdružovány do tzv. projektů. U verzí starších než 4,00 tyto projekty mohli obsahovat pouze jeden výsledný soubor (DLL, EXE, OVL, COM), u novějších již jeden projekt sdružuje libovolný počet těchto výsledků (target). Každý target může obsahovat (i u starších verzí) více textových souborů (.cpp, .c, .rc, .def). V každém souboru si můžete definovat jenom určitou množinu funkcí, objektů,... , které spolu nějak tématicky souvisí (to není podmínkou, ale je to hlavní důvod tohoto dělení na více modulů). Při programování pro Windows se nejčastěji používá každý soubor pro deklaraci nějaké třídy(struktury). Obecná definice třídy (uvedeny pouze prototypy funkcí, definice proměnných bez počáteční inicializace a pod.) se napíše do nějakého hlavičkového souboru. Ten se potom uvede na začátku modulu, ve kterém jsou potom jednotlivé funkce popsány. Když budete v kterémkoli jiném modulu (pouze v rozsahu targetu!) potřebovat tuto třídu (funkci), uvedete na začátek modulu, že se má vložit hlavičkový soubor s obecnou definicí a můžete pohodlně používat vše, co je definováno v jiném modulu. K objektově orientovanému programování se dostanu v příštím díle.
Řetězce
Řetězcová proměnná v C++ nemá vyhrazený speciální typ. V C++ řetězec je úplně jiný než např. v Pascalu. Protože řetězec je pole znaků, stačí zadefinovat pole typu char požadované délky.
Příklad:
char Retezec1[255];
char Autor[]="Marek Libra";
Za poslední znak řetězce se dává znak 0. Funkce určené k operacím s řetězci tuto hodnotu zaplňují sami, pokud ale řetězec zaplňujete vy (skládáním jednotlivých písmen), dejte si pozor, abyste na tuto hodnotu nezapomněli. Díky této hodnotě je potřeba vždy zadefinovat znakové pole o 1 znak větší..
Díky tomu, že řetězec je pole znaků, není správný zápis nebo jemu podobné:
char String1[26];
char String2[26]="Nějaký text";
String1=String2; // Chyba!!!
Tento zápis je chybný. Kopírování řetězce se provádí přes funkci char *strcpy( char *str1, *char str2 ), která zkopíruje řetězec str2 do řetězce str1 a vrátí ukazatel na str1. Tato funkce je definována v souboru string.h. Za parametr má 2 ukazatele na řetězce. Protože jsem již o ukazatelích v některém předcházejícím čísle psal, opakovat to nebudu, ale bylo by vhodné si ten článek přečíst, protože dále s tím budu počítat.
Pro formátovaný vstup a výstup má řetězec kód %s (scanf("%s", &str ); ).
Další funkce pro práci s řetězci
Zde uvádím seznam některých dalších často používaných funkcí:
Funkce:
Význam:
int strlen( char *str ) Vrácení délky řetězce bez znaku 0 na konci.
char *strcat( char *str1, char *str2) Připojení str2 k str1. Vrací se ukazatel na str1.
char *strchr( char *str, char znak ) Vrátí ukazatel na první výskyt znaku "znak" v "str". Pokud se znak nevyskytuje, vrátí se NULL (konstanta pro 0 ).
int strcmp( char *str1, char *str2 ) Porovnání řetězců. Záporné číslo, je-li lexikograficky str1 menší než str2, a kladné číslo, je-li str1 větší než str2. Jsou-li oba řetězce stejné, vrací se 0.
char *strstr( char *str1, char *str2 ) Nalezení podřetězce v řetězci. Vrátí ukazatel na první písmeno podřetězce str2 vyskytujícího se v str1. Pokud str2 není v str1, vrací se NULL.
int atoi( char *str ) Konverze řetězce na číslo (typ int).
long atol( char *str ) Řetězec str na číslo long.
double atof( char *str ) Řetězec str na číslo double.
char *itoa( int, char *str, int Cislic ) Int číslo do řetězce str. Cislic udává, kolika ciferné číslo se konvertuje. Vrací se ukazatel na první znak ve zkonvertovaném řetězci.
char *ltoa( long, char*str, int Cislic ) Jako předchozí, ale konvertuje se číslo typu long.
char *ultoa( unsigned long, char *str, int Cislic ) Jako předchozí, ale konvertuje se číslo typu unsigned long.
Uváděl jsem pouze nepoužívanější funkce. Pro kompletní výpis funkcí se podívejte do helpu.
Objektově orientované programování
V dnešním díle našeho seriálu bych se chtěl věnovat objektově orientovanému programování (dále jen OOP). Zdůrazňovat praktický význam tohoto moderního způsobu je zřejmě v tomto časopise zbytečné, rovněž předpokládám, že znáte alespoň obecnou terminologii kolem OOP. Pokud ne, rád se k tomu vrátím i třeba ve speciálním článku, protože pochopení technik OOP považuji (a jistě nejen já) za v dnešní době velmi důležitou věc.
Většina z OOP je novinkou v C++, standardní C s tím pracovat neumí (až na drobnosti).
Deklaraci objektu lze v C++ provést dvěma způsoby - buď pomocí klíčového slova class nebo struct. Pro definici nového objektu se používá zadefinování nového typu k němuž se později přiřadí proměnná - požadovaný objekt. Obecně se deklarace dá napsat takto:
struct|class typ
{
// složky
} objekt;
Tedy, na začátek uvedete klíčové slovo struct nebo class (k rozdílu mezi nimi se dostanu později), za ně název nového objektového typu typ, za něj do složených závorek definici složek (metod nebo položek), za tento definiční blok složenou závorku a jméno nového objektu (objektové proměnné). Objektových proměnných může být zadefinováno i více, vždy však odděleny čárkou. Na konec "výčtu" jmen proměnných se dává středník (musí se uvést, i když jména proměnných neuvádíte).
Při definici můžete vynechat jméno typu nebo objektové proměnné (nebo obou). Pokud vynecháte typ a uvedete proměnné, zadefinují se proměnné, ale pokud budete chtít deklarovat někde na jiném místě v programu další objektovou proměnno se stejnou strukturou, budete muset znovu opsat celou definici objektu. Vynecháte-li jméno proměnné a uvedete-li jméno typu, můžete později pomocí definice
typ proměnná;
/* kde typ je objektový typ shora deklarovaný a proměnná je nový objekt */
zadefinovat novou proměnnou s platností (díky takto napsané definici) pouze v aktuálním bloku. Pro definování objektových proměnných platí to samé jako pro definování normálních. Vynecháte-li jméno proměnné, musíte (stejně jako by tam byla) uvést za koncovou složenou závorku (}) středník.
Pokud vynecháte typ i proměnnou, překladač Vám chybu neohlásí (maximálně nějaké varování, že je to blbost), ale nezadefinujete vůbec nic, takže takováto definice je absolutně nelogická.
Rozdíl mezi objektem definovaným pomocí klíčového slova struct a class je především v počátečním nastavení přístupových práv pro složky (viz. dále). Struct má standardně nastavena práva na public, class na private. Dále je možné vytvářet šablony pouze ze tříd (šablona - další vymoženost jazyka C++ - viz. někdy příště).
Přístupová práva ke složkám objektu jsou tři:
• public: K takovýmto složkám lze přistupovat volně odkudkoli z programu, kde má objekt platnost
• private: K private-složkám lze přistupovat pouze "zevnitř" objektu, položka je platná pouze v daném objektu, potomek k ní přistupovat nemůže
• protected: Tyto složky jsou přístupné pouze "zevnitř" aktuálního (rodičovského) objektu nebo z potomka
Slova public, private, protected jsou klíčová.
Uvnitř objektu se může vyskytovat několik "bloků" uvozených klíčovými slovy. Příklad:
struct typ
{
// složky nastavené na public - standardní nastavení na začátku struct-objektu
private:
// private složky
protected:
// protected složky
private:
// private složky
public:
// public složky
public:
// zase public složky. Tato definice má spíše estetický význam (např. si můžete rozdělit metody a položky)
};
Občas, není to sice tak časté, ale vyskytne se to, je potřeba zadefinovat typ, lépe řečeno, říci, že něco takového se bude používat, ale vlastní definici složek uvést až dále v programu. C++ to umožňuje:
struct|class typ;
Takto definovaný typ se "na oko" jeví, jako by neměl žádné složky, ale ty se později dají dodeklarovat. Ovšem definice
struct|class typ { };
neprovede to, co po ní chceme, tedy umožnit nám později dodělat další složky. Takto se zadefinuje pouze typ bez položek, což nemá praktický význam.
Klíčové slovo friend
Tento příkaz umožňuje přístup ke složkám nebo některým objektům s atributy private nebo protected. Tímto slovem můžete označit složku (objekt), která bude mít právo k tomu, aby mohla měnit všechny složky objektu.
Příklad:
struct A
{ /* složky */ };

void HodnaFunkce();

class B
{
friend A;
friend HodnaFunkce();
/* vlastní složky třídy B */
}
Tímto byla zadefinována struktura A a funkce HodnaFunkce(). Dále bylo udáno, že všechny složky ze struktury A a funkce HodnaFunkce() mají volný přístup ke složkám ze třídy B, ať mají nastavena jakákoli práva.
Definice složek objektů
Nyní je na čase, abychom si řekli něco o tom, jak definovat složky objektu. Nejprve položky (angl. data members), tedy proměnné, pole a podobně. Definice položek s příslušností k nějakému objektu je skoro stejná jako definice položky v normální části programu. Uvedl jsem skoro. Malý rozdíl je - položka má platnost pouze v rámci práv vyhrazených klíčovými slovy (public, protecetd, private). K jednotlivým public-položkám samozřejmě můžete přistupovat "zvenčí", stejně jako v Pascalu nebo kterémkoli jiném jazyku s objektovou podporou. Jak se definují proměnné nebo pole zde uvádět nebudu, psal jsem o tom v některém předcházejícím čísle, rovnou přistoupím k příkladu:
struct B
{
int Pocitadlo;
};

struct A
{
char Znak;
int Cislo;
long double Pole[255];
float *Ukazatel;
B StruktB;
};
V tomto příkladu jsem vytvořil strukturový typ A, který má 5 položek - Znak (znaková proměnná), Cislo (proměnná typu int), Pole (pole s 255 prvky typu long double), Ukazatel (ukazatel na nějakou float hodnotu) a StruktB (objektová proměnná typu B). Nakonec jsem nadefinoval vlastní strukturu struktury (zní to blbě, ale význam to dává) B.
Ovšem objekt bez metod (angl. method) by ve většině případů nám byl na nic. Proto se kromě položek do objektu přidávají i metody (funkce, konstruktory a destruktory). Jejich deklarace je opět skoro stejná jako deklarace funkcí jinde v programu (napsal jsem pouze funkcí, protože se samozřejmě destruktory a konstruktory nedají ani jinde deklarovat, než v objektech). Rozdíl oproti "normálu" je stejný jako u položek - volání "zvenku" objektu je povoleno pouze pro public-metody. Potomek objektu může volat pouze metody a položky nastavené na public nebo protected (naplatí pro přátele - friend).
Deklarace konstruktoru a destruktoru je snadná. Provádí se stejně jako deklarace funkce, ale neuvádí se typ návratové hodnoty (jinými slovy, konstruktor, stejně jako destruktor, nevracejí žádnou hodnotu). Konstruktorů a destruktorů může být v jednom objektu více, při definování objektu se potom automaticky zvolí ten, který odpovídá zadaným parametrům (blíže vysvětlím později). Z toho plyne, že stejně jako u funkcí se v 1 objektu nemůžou vyskytnout 2 konstr. a destr. se stejnými parametry. Pokud při deklaraci objektu je (konstruktor a destruktor) neuvedete, překladač je dodělá sám, automaticky. Oba budou mít tělo bez příkazů.
Jméno konstruktoru a destruktoru je vždy stejné se jménem objektového typu. Pro následující příklad budeme uvažovat strukturu A, jejíž implicitní nastavení public není změněno.
Deklarace konstruktoru:
struct A {

A( /* parametry */ )
{ /* tělo konstruktoru */
}
};
Kde A je jméno konstruktoru (shodné se jménem objektového typu), za /* parametry */ můžete (a nemusíte) dosadit parametry konstruktoru. Pokud žádné neuvedete, později nový objekt můžete nadeklarovat úplně normálně:
A ObjektA;
Uvedete-li např. parametr int Cislo, deklarace by pak vypadala třeba takto:
A ObjektA( 25 );
Uvedete-li jeden konstruktor bez parametru a druhý téhož objektu s nějakým parametrem, uvádí se při deklaraci objektu vždy závorka, tedy:
A ObjektA1(); // Použije se konstruktor bez parametrů
A ObjektA2( 10 ); // Použije se konstruktor s parametrem int Cislo
Deklarace destruktoru
je stejná a platí pro ni stejná pravidla jako pro deklaraci konstruktoru, jenom se před jméno dává znak "~" (vlnovka).
Příklad se vztahuje ke stejnému objektu jako je uveden výše:
~A( /* parametry */ )
{ /* tělo destruktoru */
}
V objektu může pochopitelně být konstruktor a nemusí být destruktor (nebo obráceně). To ovšem není moc časté a často se uvede pro přehlednost alespoň jméno, i když za ním bude následovat prázdný programový blok (kousek za sebou uvedené složené závorky).
Deklaraci funkcí rozebírat nebudu, jenom uvedu příklad:
struct A
{
void FunkceA( int Cislo );
int B( int );
int C;
};
V tomto příkladu jsem použil neúplnou definici, takže někde dále budu muset uvést ještě tělo jednotlivých funkcí. Neúplná definice se používá samozřejmě i u konstruktorů a destruktorů.
Operátor :: (příslušnost k objektu)
Tento operátor říká, že nějaká složka má příslušnost k nějakému objektu. Toho se využívá například při "dodělávání" těla metody.
Příklad (z výše uvedené definice):
void A::FunkceA( int Cislo )
{
printf("\n Zadáno číslo: %d", Cislo );
}
Tedy, nejprve se uvede typ návratové hodnoty (u konstruktoru a destruktoru nic!), potom objektový typ, ke kterému má mít metoda příslušnost, za operátor :: se uvede jméno funkce s parametry (tyto parametry metod mohou být stejně jako u "normálních" funkcí předdefinované, tedy u nich lze udat implicitní hodnotu, ale o tom jsem se už asi zmiňoval).
Velikost objektů
Velikost objektu závisí na celkovém součtu velikostí všech jeho položek. Ke zjištění velikosti slouží operátor sizeof. Ten je možno použít jak pro (objektovou) proměnnou nebo i pro zjištění velikosti typu. Syntaxe je:
sizeof( /* parametr */ );
Za /* parametr */ se zadává buď jméno proměnné nebo jméno typu. Vrací se celková zabíraná délka.
Pomocí operátoru :: můžete také určit i velikost jednotlivé položky:
sizeof( A::C );
Používání složek objektů
Na začátku je samozřejmě potřeba vytvořit nějaký objekt pomocí objektového typu.
Objektový typ:
struct typ
{
public:
typ();
~typ();

int Cislo;
char Znak;
void Funk1();
double Funk2( char );
protected:
int X;
int Funk3();
private:
int Y;
};
typ Objekt;
Tak, a nyní máme vytvořen objekt Objekt, se kterým budeme nyní pracovat. Tento objekt je pouze demonstrační, tedy neuvádím těla jednotlivých metod, pokud to není součást příkladu.
Nejprve vytvořím tělo konstruktoru:
typ::typ()
{
Cislo=0;
Znak='a';
X=Y=5;
}
Zde jsem udal, že hodnoty proměnných Cislo, Znak, X, Y budou vždy po definici nějaké nové objektové proměnné typu typ mít určité hodnoty.
A teď destruktor:
typ::~typ()
{ };
Tento destruktor nekoná žádnou činnost, má prázdné tělo, pro náš příklad nepotřebuje nic dělat.
Stejným způsobem by se dali vytvořit i další metody objektu, ale to již potřebovat nebudeme. V konkrétním programu byste ale museli definici uvést, linker by totiž ohlásil chybu.
Jak jste si asi všimli u konstruktoru, volá-li se nějaká složka, která je uvnitř objektu, neuvádí se žádná příslušnost, překladač si ji určí sám (je mu to jasný). U takovéhoto volání by se však měl uvádět operátor ::, ale pro praktický význam nemá smysl, tak se neuvádí. Uvádí se pouze u potomků a to je z "estetického" důvodu nebo pokud se položka (metoda) v bázové třídě nazývá stejně jako v objektu potomka (ve kterém zrovna jsme) a my chceme přistupovat k rodičovské.
Voláte-li nějakou složku "zvenčí", slouží k tomu dva operátory:
. (tečka) -> (pomlčka s znakem "je větší")
Rozdíl mezi nimi je pouze v tom, zda objekt, od kterého se "odpichujeme" je ukazatel nebo proměnná. Nejlépe to asi vysvětlí příklad:
typ Obj1; // Obj1 je objektová proměnná
typ *Obj2; // Obj2 je ukazatel na objekt

Obj2=&Obj1;
// Obj2 nyní ukazuje na prostor rezervovaný Obj1, přístup k tomuto prostoru však bude jiný
Chceme-li volat funkci Funk1(), dá se to provést takto:
Obj1.Funk1();
// Zavolá se funkce Fuk1() z objektu Obj1
nebo
Obj2->Funk1();
// Zavolá se funkce Funk1(), uložená v paměťovém prostoru objektu Obj1
Rozdíl je nyní asi každému jasný - operátor . (tečka) se používá pro přístup ke složkám skalárních objektů a operátor -> pro přístup ke složkám objektů definovaných ukazatelem.
Bitové položky objektů
V objektu se často vyskytují proměnné, kterým stačí pro správné určení všech hodnot, kterých mohou nabývat (např. bool - stačí pouze 1 bit, hodnoty 0 nebo 1). Zbytek prostoru (dopočítáváno do velikosti daného typu - u int do 2 bajtů) je reservován, ale z logického hlediska se nikdy nevyužije. Nyní si možná říkáte, no a co, tak jsou paměťové nároky programu o 3/4 bajtu větší, než je nezbytně potřeba. Ale uvažujte, těchto položek máte třeba 50 - můžete například pomocí objektu popisovat nějakou desku s přepínači, u nichž platí: zapnuto (1) nebo vypnuto (0). A nic mezi. Pak už budou tyto ztráty paměťového prostoru větší. Ještě víc se to může projevit, uděláte-li z tohoto objektu pole o několika prvcích. Jistě už nyní i každého programátorského zelenáče napadne, jak toto nevyužité místo minimalizovat. V C++ se to dá udělat velice snadno - pomocí bitových položek.
Bitové položky jsou položky, u nichž uvedete, kolik bytů mají v paměti zabírat, čímž pochopitelně omezíte i jejich hodnotový rozsah.
Příklad definice bitové položky, které zabírá pouze 1 bit:
unsigned char Logicka : 1;
Tato proměnná bude moci nabývat pouze hodnot, které je možno popsat ve dvojkové soustavě 1 cifrou (pokud dobře počítám, jsou to hodnoty 0 a 1). Dříve jsme si uváděli definici typu bool pomocí výčtového typu enum. Každý typ definovaný pomocí výčtového typu enum, zabírá 2 bajty (vychází z typu int). A teď srovnejte velikost proměnné Logicka s proměnnou definovanou pomocí bool. Logicka zabírá mnohem méně a výsledek je stejný.
Tyto položky nejsou pouze vymožeností C++, i Pascal je zná (přes set).
Přímé určování velikosti bitové položky pomocí operátoru sizeof není možné, určení velikosti objektu využívajícího bitové položky možné je. Je-li výsledná hodnota v necelých bajtech, zaokrouhluje se vždy nahoru (nějaký jiný objekt již nemůže využívat prostor v bajtu zbylý po bitových položkách z předcházejícího, ale stejně je to úspora).
Nyní k organizaci těchto položek v paměti. Jsou ukládány v paměti přesně opačným způsobem, než normální. Máte-li 5 položek, přičemž každá pro ulehčení zabírá 3 bity, bude uložení v paměti vypadat následovně (schematicky!):



Uložení normálních proměnných objektu by probíhalo od prvního bitu prvního bajtu. U bitových položek se ukládá obráceně. V případě, že k těmto položkám budete přistupovat standardně přes operátory "." a "->", nemusí vás to zajímat. Pokud ale budete k nim přistupovat např. přes ukazatele, je potřeba to brát v úvahu (nelze použít "standardní" ukazatelovou aritmetiku).
Operátor this
Tento operátor je velmi důležitý. Je to v podstatě něco jako ukazatel, který vždy ukazuje na počáteční adresu objektu. Využívá se toho třeba když potřebujete předat nějaké externí funkci, pracující s daty vašeho objektu, adresu "aktuálního" objektu (programování pro Windows, bez toho se dnes už snad ani nehnete).
V příštím díle ještě trochu rozvedu objekty. Pokud se k tomu dostanu, dám na ně už (konečně) nějaký praktický příklad.
Sice se budu opakovat, ale napíši to raději ještě jednou. Máte-li zájem o učení jazyka C++ a s něčím si nevíte rady (konkrétně třeba dnes jste se nemuseli vyznat v terminologii kolem OOP nebo v něčem jiném), napište mi. Pokud vznikne nějaký dotaz, zařadím ho rád do dalšího dílu. Neznáte-li základy OOP, je to na speciální článek. Dále pokud máte připomínky k mému výkladu (jdu moc rychle, nevyznám se v tom a pod.), budu rád, pokud se to ke mně také dostane.
Reference
Reference nepatří pouze k OOP, lze je využívat i u běžného programování (teď ale záleží na tom, co považujete za "běžné programování"). Jedná se v podstatě o druh datového typu (nebo něco na ten způsob). Mají velice blízko k ukazatelům. Asi nejčastější uplatnění najdou u parametrů volaných odkazem. Jejich definice je jednoduchá, stejná jako u ukazatelů, jenom se místo * (hvězdička) dává &. Podmínkou je, že reference musí být ihned po deklaraci inicializována.
int Prom, &Ref=Prom;
Nyní máme zadefinovanou proměnnou Prom a referenci Ref na typ int odkazující na proměnnou Prom. Nyní, ať provedeme jakoukoli operaci s Ref projeví se to na hodnotě Prom. To platí i opačně.
Příklad:
Ref = 12; // Totéž jako: Prom = 12
Prom = 0; // Totéž jako: Ref = 12
Referenci je samozřejmě možné při inicializaci dát určitou konstantní hodnotu. V tomto případě se v paměti vytvoří oblast o velikosti nutné k popsání hodnoty daného typu. Další práce s referencí je shodná, jako s konstantou, tudíž se to moc nepoužívá, lepší (spíš přehlednější) je používat konstant (klíčové slovo const).
Příklad:
int &ref = 10;
Je možné odkázat referenci na jiný typ než je sama. V tomto případě se v paměti vytvoří dočasně prostor (po dobu platnosti reference) a do něho se zkonvertuje obsah proměnné. Je zde ale háček - změna hodnoty reference již nemá vliv na hodnotu původní proměnné (a naopak).
Příklad:
int Prom=50;
char &Ref=Prom;
Ref = 100; // Prom je pořád 50 !!
Až na to, když se referenci přímo přiřadí hodnota nebo je reference jiného typu než proměnná (i když to má občas smysl, např. u funkcí, u kterých nevíte, co dostanete za parametr), je hlavní výhoda a smysl jejich používání v tom, že šetří paměť a není s nimi tak složitá práce jako s ukazateli (které mají navíc i některá omezení). Kdo to ještě nepochopil - pomocí referencí se předává pouze odkaz (odtud název reference, reference=česky odkaz) na již vytvořený paměťový prostor. U jednoduchých typů, které jsem například uváděl zde, by snad ani nevadilo, že se v paměti vytvoří další 2bytový prostor, ale vezměte si takovou velkou strukturu, zabírající kolem 10Kb v paměti popř. přímo seznamy nebo kontejnery (co to je, k tomu se dostanu později), to už je zase o něčem úplně jiném.
Dědičnost objektu
Dědění objektu (-ů) je jednoduché. Potřebujeme k tomu samozřejmě předkovský objekt a název budoucího potomka. Potom se to provede nějak takto:
<struct | class > potomek : <přístupové právo> předek
{
// Přidávané složky
} obj_typ;
Opět si musíte vybrat, jaký má být potomek - zda struktura nebo třída (platí pro to to samé, jako u "nedědění", praktický rozdíl je tedy hlavně v počátečním nastavení přístupových práv). Potom následuje název potomka, za ním dvojtečka (: ), za ní přístupové právo pro složky předka a za tím jméno předka nebo předků (odděleno čárkou). Přístupová práva jsou zase 3 - public, protected, private. K složkám předka potom budete přistupovat takovým způsobem, jakým vám povolí dané právo. Práva přístupu lze však pouze zúžit, niko-li rozšířit. Tedy je-li v potomku public-složka, a potomek dědí po předkovi s private právy, k dané složce se potom můžete z hlediska potomka chovat pouze jako k private-složce. Bude-li to však naopak (předek - private-složka, potomek dědí s public), ke složce můžete přistupovat pouze jako k private. Z toho plyne nejčastěji používané právo při dědění - public.
Za jménem předka následuje programový blok s výčtem nových složek, které bude mít potomek oproti předkovi navíc. Za blokem je opět objektová proměnné zakončená středníkem.
Vyskytne-li se v potomkovi stejný název a typ složky jako má předek, bude se implicitně pracovat s potomkovou. Na předkovu se "sáhne" pouze v případě, že to výslovně řeknete (to zní pěkně blbě, když se programy obyčejně datlují na klávesnici). To můžete udělat následovně:
struct A
{
int Prom;
};

class B
{
int Prom;
};

class C : public A, B
{
int Prom;
void Funkce();
};

void C::Funkce()
{
Prom = 5; // Bere se proměnná z C
A::Prom = 15; // Bere se proměnná z A
B::Prom = 45; // Bere se proměnná z B
}
Myslím, že je to jasné. Jenom ještě jednu poznámku - z příkladu plyne, že dědit může i struktura od třídy a naopak.
Virtuální metody
Praktický význam popisovat nebudu, pokud vím ve všech objektových jazycích je velmi (ne-li úplně) stejný. Virtuální metodu zadeklarujete pomocí klíčového slova virtual:
struct A
{
virtual void Funkce();
};
void A::Funkce() { /* Tělo */ }
Zde je vidět, že virtual se uvádí pouze jednou a to při deklaraci v objektu (nezáleží na tom, zda napíšete tělo hned nebo až potom). Dopisujete-li tělo mimo blok objektu, virtual se již neuvádí (nesmí). Takto definované funkce lze volat i explicitně pomocí operátoru :: (2x dvojtečka).
Při používání virtuálních metod se vytváří tabulka virtuálních funkcí, kde jsou zaznamenány odkazy na všechny virtuální funkce použité v programu. Při běhu (z pochopitelných důvodů při překladu nemůže překladač přesně vědět, kterou chcete) se při zmínce o virtuální metodě podívá do tabulky a podle daného odkazu zavolá příslušnou metodu. Tato tabulka se vytváří v datovém nebo kódovém (programovém) segmentu, ale to vás víceméně nemusí tolik zajímat, přistupuje se k tomu stejně.
Objekt odkazující sám na sebe
Jedná se o objekt A, který obsahuje ukazatel typ A.
Příklad:
struct A
{
A *predch, *nasl;
// složky
};
Této nebo podobné deklarace se používá například u seznamů, kdy nevíte, kolik položek budete muset do paměti vložit. Pokud by se to řešilo přes pole o konstantní velikosti (stylem Typ Prom[50]) má to řadu nevýhod a jen pár výhod. Výhody jsou, že je to snadné napsat, pro začátečníka snadno čitelné a přístup k položce (pokud přesně znáte její polohu) je rychlý. Ovšem nevýhody převažují nad výhodami, málokdy víte, jak bude seznam skutečně dlouhý, během jeho existence se obyčejně zkracuje nebo naopak prodlužuje a pod. Vytvoření takového seznamu je v C++ velice snadné. Do výše uvedených ukazatelů stačí ukládat adresy následujícího nebo předcházejícího prvku daného seznamu. Jednotlivé prvky se vytvářejí dynamicky, tedy pomocí operátorů new a delete.
Dynamické alokování paměti
Tento způsob nemá uplatnění pouze s OOP, i když se ve spojení s OOP nejčastěji používá. Při takovémto alokování se paměť "rozvrhne" až za běhu programu a přitom se většina práce svalí na operační systém. V podstatě se alokuje v paměti oblast o určité velikosti pro použití výhradně daným programem. K takto alokovanému paměťovému bloku se potom přistupuje přes ukazatele (pokud jste plně nepochopily ukazatele a práci s nimi, nemůžete pracovat s dynamicky alokovanou pamětí). Nevýhodou, která začátečníkům dělá velké problémy, je, že po skončení práce s tímto paměťovým prostorem se musí dealokovat, aby byl použitelný pro jiné aplikace. Pokud byste to neudělali, brzy by se paměť zaplnila nevyužitelnými datovými bloky (i když Váš program již dávno nepoběží) a jediná možnost na jejich "dealokaci" je reset počítače. Operátory new a delete jsou vymoženost C++, C paměť také dokázalo alokovat, ale provádělo se to jinak (a pořád to samozřejmě jde, kvůli kompatibilitě a někdy i z jiných důvodů). V C se alokovaná paměť specifikovala počtem potřebných bytů. New se používá pro vytvoření jednoho nebo několika objektů s konkrétním typem.
Nejprve se budu věnovat alokování v jazyku C, tedy pomocí funkcí. Slouží k tomu funkce - malloc, free, calloc, realloc pro alokaci prostoru menšího než 1 16-bitový segment (64Kb) a funkce farmalloc, farfree, farcalloc, farrealloc pro alokaci většího prostoru. Jsou ale použitelné pouze pod DOSem a 16-bitovými Windows. Jsou deklarovány v "alloc.h" a "stdlib.h".
Syntaxe těchto funkcí je:
void *malloc(size_t size);
void free(void *block);
void *calloc(size_t nitems, size_t size);
void *realloc(void *block, size_t size);
void far *farmalloc(unsigned long nbytes);
void farfree(void far * block);
void far *farcalloc(unsigned long nunits, unsigned long unitsz);
void far *farrealloc(void far *oldblock, unsigned long nbytes);
Použitý typ size_t je stejný jako unsigned int (pouze předefinováno pomocí typedef). Pro zaalokování n-bytů se používá malloc (farmalloc), která alokuje size bytů a vrátí ukazatel na začátek tohoto bloku. Tato funkce je "univerzální", návratová hodnota je typu void (možná si myslíte, že se to rozchází s tím, co jsem psal dříve, ale ono to tak horký nebude, viz. dále). Při praktickém používání se typ void "přetypuje", jasnější to bude asi z příkladu:
int *pointer;
pointer = (int *)malloc(20);
Nejčastěji se takto provedená alokace používá proto, aby se do paměťového prostoru ukládaly čísla typu int (ovšem není to nutnost). Po skončení práce s tímto prostorem ho odalokujeme:
free( pointer );
Pro snazší práci existuje funkce calloc, které se zadává velikost 1 položky a počet těchto položek (ona to pak vynásobí a stejně zavolá malloc). Využívá se například pro pole. Uvolnění prostoru je zase pomocí free. Funkce realloc slouží ke změně velikosti alokované oblasti. Za parametr block se dává ukazatel na daný datový blok a size je nová velikost. Vracená hodnota je ukazatel na novou "polohu" alokované oblasti.
Operátory new a delete
Dalším a mnohem snazším způsobem alokace v C++ je použití těchto operátorů. Operátor new slouží k provedení alokace a vrací ukazatel na alokovanou oblast. Typ nové oblasti se zadává za operátor, jedná-li se o objekt s konstruktorem (s parametry) tak za typ do závorek parametry konstruktoru.
Příklad:
int *i = new int;
Přístup k takto alokované paměti (u 16-bitových překladačů 2 byty, u 32-bitových to jsou 4 byty) se přistupuje snadno přes ukazatel i. Je-li i rovno NULL (makro s hodnotou 0), paměť nebyla alokována (obyčejně z důvodu nedostatku paměti).
Zadeklarovat můžete samozřejmě i pole:
int *i = new int[10];
Oproti normálně deklarovaným polí, lze za 10 uložit i proměnnou (new konstantu). Ovšem platí, že když se později změní hodnota proměnné, nezmění se již velikost pole (to je ostatně logické). Vícerozměrné pole se může deklarovat například takto:
int **i=(int **) new int [10][50];
Kromě prvního rozměru pole, musí všechny rozměry být konstanty.
Dealokace prostoru se provádí pomocí delete:
delete i; // Pro náš 1. příklad
nebo
delete [ ] i; // Pro pole
nebo
delete [ ] i; // Pro matici
Seznamy
S poznatky z tohoto čísla seriálu byste už měli být schopni udělat zřejmě nejzákladnější datový útvar - seznam. Co tím mám na mysli - seznam si zkuste představit jako řadu bloků alokované paměti, přičemž každý takovýto blok nese určitou hodnotu, údaj a pod. .Každý prvek takovéto šňůry mimo jiné obsahuje i adresu (ve formě ukazatele) na další prvek seznamu. Někdy je výhodné vložit do prvku i ukazatel na předcházející prvek. Když je potřeba přidat nový prvek, vytvoří se v paměti (samozřejmě dynamicky) nová oblast, její adresa se uloží do ukazatele v předcházejícím prvku. Nově vytvořená oblast (tj. poslední prvek našeho seznamu) má daný ukazatel nastavený na NULL (neukazuje nikam). Díky tomu není problém tento konec určit.
K uvedené alokaci paměti se nejčastěji používá operátorů new a delete (je s nimi snazší práce).
Přetěžování operátorů
Další velmi užitečná věc. Zda se vyskytuje (nebo něco podobného) i v Pascalu nevím, tak daleko jsem v něm nedošel, ale myslím si, že alespoň něco na ten způsob tam určitě půjde. Podstata je podobná jako při přetěžování funkcí - vždy se zvolí ten, kterému odpovídají dané typu použitých hodnot. V podstatě se dají přetěžovat všechny operátory. Hlavní výhodou je, že matematické nebo logické operace lze provádět i u objektových proměnných, nejen u primitivních typů (např. int, long double). Standardně překladač přetěžuje pro každou třídu operátor = (mělká kopie, viz. dále).
Přetěžování se pokusím vysvětlit na příkladu přetěžování operátoru +:
struct Data {
int A;
int B;
int C;
};
void main()
{
Data P1, P2, P3;
P1.A=P1.B=2; // Počáteční inicializace
P1.C=5;

P2=P1; // Správně, C++ má standardně tento operátor přetížen
P3=P1+P2; // Chyba !!!!!, Překladač neví, co má sčítat
}
Tímto stylem napsaný kód je chybný - překladač neví, co a jak má kopírovat. Proto se musí přetížit operátor +, čímž se řekne, jak má sčítání 2 objektů typu Data probíhat.
Někde před funkci main() se musí uvést například tato definice:
Data operator + (Data A, Data B)
{
Data Pomoc; // Pomocná proměnná
Pomoc.A = A.A + B.A;
Pomoc.B = A.B + B.B;
Pomoc.C = A.C + B.C;
return Pomoc;
};
Nyní to již bude fungovat. Co uvedete v těle přetěžovaného operátoru samozřejmě záleží pouze na vás. Jaká bude návratová hodnota (tedy výsledek operaci při použití takto přetíženého operátoru) je také Vaše věc. Provádíte-li tuto globální definici, tedy ne s příslušností pouze k 1 konkrétnímu objektu (a popřípadě jeho potomkům), musí být dva parametry - 1. je levá strana při použití operátoru a 2. je pravá strana.
Přetěžování s příslušností k objektu by mohlo vypadat nějak takto:
struct Data
{
int A;
int B;
int C;
Data operator + (Data B)
{
Data pomoc;
pomoc = A+B.A;
pomoc = A+B.B;
pomoc = A+B.C;
return pomoc;
}
};
Zde se uvádí již pouze pravá strana operátoru, na levou je implicitně dosazován daný objekt.
Jediné operátory, které z logického důvodu nelze přetížit, jsou: . , .* , :: , ? :
Příklad na dnešní článek naleznete v souboru cpp05.cpp. I/O operace použité v příkladu jsou řešeny přes proudy. Co to je a jak se s nimi pracuje se pokusím vysvětlit v další kapitole. Pro tentokrát to zkuste brát tak, jak to je, nyní pro Vás bude zřejmě zajímavé, podívat se na to, jak se pracuje s dynamickou alokací paměti a implementací seznamu (je pouze jednosměrný. Dvojsměrný by byl složitější, i když někdy rychlejší - to ale není náš případ).

Knihovna pro I/O proudy
C/C++ neobahuje operace pro I/O (není tomu tak např. v Pascalu), aby byl jazyk snáze přenositelný mezi více platformami. Tyto funkce jsou však již některými standardy přidány, jsou ovšem přidány pomocí knihoven. Kromě běžných funkcí printf() a scanf() a jim podobných lze I/O operace v rámci OOP řešit pomocí proudů. Jednotlivé I/O operace jsou prováděny přes objekty, přes které "prochází" proudy dat. Část těchto dat bývá obyčejně uchovávány v paměťovém prostoru (bufferu). K bufferu lze přistupovat přes metody objektu.
Knihovna, která tímto způsobem pracuje a kterou zde budu popisovat, je knihovna AT&T standardně implementována v překladačích fy. Borland. Pro zápis do proudu se používá operátor << (inserter) a pro čtení >> (extraktor) - jsou to přetížené operátory bitového posunu.
Obecná syntaxe je:
proud << prvek;
Do proudu proud se zapíší data z objektu prvek. Extraktor a inserter je standardně v knihovně přetížen pro primitivní typy a pro některé další objekty. Ve svých aplikacích jej pravděpodobně budete vždy také přetěžovat pro vlastní typy dat.
Knihovna pro práci s I/O proudy tvoří složitou (nikoli však nepochopitelnou) hiearchii děděných objektů. Základní třída streambuf obsahuje souhrn všech společných znaků pro všechny typy proudů. Je to např. vyrovnávací paměť (buffer). Buffer zvolené délky se vytváří konstruktorem
streambuf( char *kde, int delka );
Od této třídy jsou odvozeny další třídy pro diskové operace filebuf, pro znakové proudy v paměti strstreambuf a pro výstup na obrazovku conbuf.
Hiearchie tříd pro různé typy proudů vypadá nějak takto:

Třídy uvedené níže jsou potomky příslušných tříd uvedených výše.
Třída ios
Jednotlivé objekty této třídy se běžně nepoužívají přímo, jsou spíše používány prostřednictvím objektů tříd potomků. Tato třída obsahuje implementaci společných znaků I/O operací. Formát dat v peoudu určují bity x_flags, ke kterým se dá přistupovat přes funkce setf() a unsetf(). Dalším vyýznamným prvkem je pointer na paměťovou oblast rezervovanou objektem třídy streambuf (buffer).
Pro tuto třídu jsou standardně přetíženy některé operátory, takže výraz
!stream je 1 vždy, když dojde k chybě uvnitř proudu.
Významná metoda je také eof() - vrací 1, když bylo v proudu naraženo na speciální znak konce souboru.
Třídy istream, ostream
rozšiřují schopnosti třídy ios o funkce specifické pro I/O operace - istream pro I a obráceně. Až od této úrovně naší hiearchie jsou přetíženy operátory << a >> (viz víše). Před vytvořením objektů těchto tříd je třeba vytvořit ještě buffer o určité délce (třída streambuf nebo její podtřídy). Ukazatel na tuto oblast je parametrem konstruktorů tříd. Elementární prvky těchto proudů jsou typu char. Z istream bych jmenoval např. metodu get() vracící znak na aktuální pozici v proudu u ostream jí odpovídá put(). Tyto metody jsou hodně přetěžovány na různé typy dat. Metoda flush() vyprázdní buffer a seek() nastaví aktuální pozici v proudu.
Třída iostream
implementuje proud pro obě I/O operace. Konstruktor má rovněž za parametr ukazatel na vyrovnávací paměť.
Třídy istream, ofstream a fstream
třídy specializované na diskové operace (souborové). V programech se často používají objekty těchto tříd (narozdíl od předchozích, až nyní je práce snažší, přestože se stále jedná spíše o elementární operace). Velůká výhoda je v tom, že obsahují i přetížený operátor bez parametrů vytvářeující vyrovnávací paměť - nemusíte ji vytvářet sami. Před prací s proudem se musí soubor otevřít metodou open() a po skončení uzavřít metodou close().
Třídy istrstream, ostrstream, strstream
jsou specializované na operace se znakovými řetězci. Zapisujezte-li do proudu typu ostrstream hodnotu neznakového typu, provede se konverze na znakový řetězec, který se uloží do do bufferu od aktuální pozice (např. číslo typu int se zkonvertuje z binární podoby na znakovou: 123 => "123"). Při opačné činnosti - čtení z istrstream se konvertuje zpět ze znakové na binární (případně jinou). Třída istrstream má za parametr ukazatel char* ukazující na existující buffer v paměti (spravidla bývá již naplněn řetězcem - potom se konvertuje na určitý typ). Jedno z přetížení konstruktoru ostrstream() je bez parametru - automaticky vytváří buffer (dynamicky).
Třída constream
se spacializuje na výstup na obrazovku konstruktor je bez parametrů. Jmenujme např. metodu clrscr() s návratovou hodnotou typu void, která vyčistí obrazovku a metodu window( int left, int top, int right, int bottom ) stejného typu umožňující definovat aktuální výřez obrazovky.
Pro přesný popis jmenovaných a ostatních metod doporučuji nahlédnout do helpu. Nemá smysl ho zde citovat, když jej máte pravděpodobně každý u své verze překladače. Velice jednoduchý příklad je v příkladu z minulého čísla (jedná se pouze o základní operace pro čtení a zápis spolu s přetížením inserteru a extraktoru).
Slovo na možný závěr
Nedávno jsem četl v CW recenzi na DJGPP - překladač C++. Jedná se o freeware program s ne moc vysokými hardwarovými nároky (na C++ překladač). Podle recenze je výborný, daleko lepší než Borladský nebo od Watcomu, jediná nevýhoda je, že standardně nepodporuje Windows (tuto podporu si ale není problém doprogramovat ...).Překlaádá i pro 32-bit (v tom je jeho největší síla), má výborné knihovny pro grafiku, zvuk, periferie a pod. . Pokud se mi podaří sehnat tento překladač v rozumné době, skusím napsat recenzi. Možná si říkáte, je to freeware, to bude stát za houby. Vyvracet Vám to zatím nemohu (doma to nemám), ale můžu říct, že autoři hry Quake si to určitě nemysleli. Na Internet přístup nemám, takže kdy recenze bude, je zatím ve hvězdách. Pro zájemce zde uvádím adresy, kde je možno překladač najít (.zip soubor typuji na 10-15MB).:
ftp://ftp.delorie.com/
2 mirrory v ČR:
ftp://ftp.kolej.mff.cuni.cz/
ftp://ftp.vse.cz

Obsah
Historie C++ 1
Struktura programu 1
Deklarace funkce 2
Výpis na obrazovku 3
Základní typy proměnných 4
Definice proměnných 5
Konstanty 5
Operace proměnnými 6
Vstupy 7
Deklarace nového typu 8
Řízení běhu programu 8
Podmínky 8
Cykly 9
Skoky 10
Pole 11
Preprocessor 12
Testování existence a obsahu makra 12
Direktiva #include 13
Makra s parametry 14
Direktiva #error 14
Předdefinovaná makra 14
Přetěžování funkcí 15
Funkce definované v jiném modulu 15
Řetězce 15
Další funkce pro práci s řetězci 16
Objektově orientované programování 16
Klíčové slovo friend 18
Definice složek objektů 18
Operátor :: (příslušnost k objektu) 20
Velikost objektů 20
Používání složek objektů 20
Bitové položky objektů 22
Operátor this 23
Reference 23
Dědičnost objektu 24
Virtuální metody 25
Objekt odkazující sám na sebe 25
Dynamické alokování paměti 26
Operátory new a delete 27
Seznamy 27
Přetěžování operátorů 27
Knihovna pro I/O proudy 29
Slovo na možný závěr 30
Obsah 32
Cesta k C++ 33



Cesta k C++
Seriál pro WWW stránky Studna - Programátorské zdroje.
Přílohy:
cpp01.cpp
cpp02_01.cpp
cpp02_02.cpp
cpp03_01.cpp
cpp03_02.cpp
cpp05.cpp

Copyright © Marek Libra, 1997, 1998
Autor: Marek Libra, [email protected]
Redakce: Hynek Sládeček, [email protected]





http://www.czechia.cz/studna/ Edited by Zachy

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

Sign in to follow this  

×