Kurz C++ (5.) Vítám Vás na dalším pokračování našeho kurzu o C++. Dnes si povíme něco o funkcích. ZákladyFunkce nám umožňují napsat kód, který provádí nějakou operaci, pouze jednou, a pak se na něj odkazovat z jakéhokoli místa v programu (programátorsky se tomu říká "zavolat funkci"). Můžeme tak provádět stejnou činnost na různých místech v programu aniž bychom pokaždé museli napsat stejný kód. Funkce také vnášejí do kódu modularitu a řád a dělají program přehlednějším. Funkce může získat od volajícího programu nějaké informace (říká se jim "parametry funkce"), a může mu nějakou informaci vrátit (to je "návratová hodnota"). Jak parametry, tak návratová hodnota mohou být jakéhokoli datového typu. Před použitím funkcí si musíme ujasnit pojmy deklarace a definice.
Deklarací říkáme kompilátoru, že někde dále v programu voláme takovou
funkci, a dáváme mu k dispozici příslušné údaje o ní (jméno, seznam
parametrů, typ návratové hodnoty). Příklad dvou deklarací:
Na prvním místě je typ návratové hodnoty, v našem případě funkce min vrací celé číslo, funkce vypisCopyright nevrací vůbec nic - to je význam klíčového slova void. Dále následuje jméno funkce (min, vypisCopyright) a pak v kulatých závorkách seznam parametrů (uvádí se datový typ a jméno parametru - funkce min má tedy dva celočíselné parametry (a, b), funkce vypisCopyright nemá žádný parametr. Takovému řádku se říká "hlavička funkce". Poznámka: V některých programech se můžete setkat s deklarací, která vypadá následovně:
tedy místo prázdného seznamu parametrů je klíčové slovo void. Je to naprosto totéž jako příklad vypisCopyright výše (funkce bez parametrů), ale v jazyce C (předchůdci jazyka C++). Všimněte si, že deklarací ještě neuvádíme, co vlastně bude funkce provádět. To kompilátoru říkáme až definicí:
Definice tedy začíná opětovným uvedením hlavičky, ale místo středníku na konci již píšeme kód, který se ve funkci vykonává, uzavřen do složených závorek ("tělo" funkce). V příkladu jsme použili další klíčové slovo jazyka C++, a sice příkaz return. Tímto příkazem říkáme: tady skonči provádění funkce, vrať hodnotu a předej řízení nadřazenému programu (který funkci zavolal). Proběhne-li příkaz return, další příkazy, které za ním mohou následovat, se neprovedou. Návrat do nadřazeného programu také probíhá samovolně na konci funkce. Pokud funkce nic nevrací, return tam ani nemusí být (viz funkci vypisCopyright). V opačném případě je použití return i s nějakou návratovou hodnotu povinné (funkce min). Použítí funkce v programu vypadá takto:
Proměnné výsledek jsme přiřadili návratovou hodnotu funkce
min. Funkci
můžeme použít na všech místech, kde kompilátor očekává výraz stejného
datového typu, jako je její návratová hodnota, přičemž funguje implicitní
i explicitní konverze. Funkci s návratovým typem void můžeme používat jako
příkaz, (v jazyce Pascal existuje poměrně výstižný termín "procedura") ale
nesmíme zapomenout na závorky. Vlastně jakoukoli funkci můžeme používat
jako proceduru, tedy ignorujeme návratovou hodnotu. V minulém díle jsme
mluvili o funkci strcpy. Ta je deklarována takto:
Funkce spojí oba řetězce do řetězce str1, který pak vrací. Nás
pravděpodobně nezajímá návratová hodnota, protože řetězec
str1 máme někde
deklarovaný, takže ji můžeme ignorovat a zavolat funkci takto:
Dále si všimněte, že funkci můžeme deklarovat i definovat kdekoli v programu (ale mimo jinou funkci - deklarovat například ve funkci main jinou funkci by nešlo).
Poznámka: z tohoto příkladu je vidět, že veškerý kód programu je uzavřen v nějaké funkci, mimo ni nemůžete psát kód, pouze deklarovat proměnné a konstanty. Takže tvrzením "program volá funkci" jsem se dopustil malé nepřesnosti (z důvodu jednoduchosti výkladu): ve skutečnosti je vždy funkce zavolána jinou funkci (až na funkci main, která je zavolána operačním systémem). Výše uvedený příklad na používání funkce je možná trochu zarážející,
protože chybí deklarace funkce vypisCopyright. Deklarace funkcí jsou ve
skutečnosti nepovinné. Pokud funkci definujeme před tím, než ji poprvé
zavoláme, tak deklarace je vlastně zbytečná, protože v okamžiku zavoláni
funkce kompilátor již má všechny údaje o ní k dispozici. Kdybychom
posunuli funkci min před funkci
main, mohli bychom vynechat i její
deklaraci. Nicméně existují i případy, kdy se bez deklarací neobejdeme.
Typickým příkladem jsou dvě funkce, z nichž každá volá tou druhou:
Pokusíte-li se zkompilovat tento program, kompilátor ohlásí chybu ve funkci a na řádku b(), protože o funkci b zatím neví vůbec nic (postupuje shora dolů). Poznámka: pokud Vám není jasný význam parametru stop, ten tam je pouze kvůli tomu, aby vzájemné volání funkcí a a b vůbec skončilo. Jinak by a stále volala b, následně b volala a a nikdy by to neskončilo (tedy ano: "program způsobil neplatnou operaci a bude ukončen", ale to asi nechcete). Aby nám příklad výše fungoval, musíme ho vylepšit o deklaraci funkce b
před funkcí a:
Takové případy, kdy budete potřebovat deklarace, ovšem nejsou příliš časté, takže se deklaracemi nemusíte obtěžovat. Stačí, když budete vědět, že existují.
Předávání parametrůPředstavte si, že byste potřebovali funkci počítající aritmetický a
geometrický průměr (obě najednou). To by znamenalo, že funkce bude muset
vracet dvě hodnoty, a přitom máme k dispozici pouze jednu návratovou
hodnotu. Nabízí se možnost použít k vracení výsledků parametry funkce.
Deklarace funkce by vypadala takto:
Má tedy 2 vstupní parametry (čísla a, b), a dva výstupní (do parametru
aritm budeme ukládat spočtený aritmetický průměr, do
geom geometrický):
Pokusíte-li se funkci použít, zjistíte, že se vypisují nějaké nesmyslné
hodnoty (a také dostanete při překladu upozornění že používáte
neinicializované proměnné aritm a
geom):
Je to tím, že existují dva způsoby předávání parametrů: hodnotou a
odkazem. Předávání odkazem se používá standardně, a znamená to, že hodnoty
parametrů se zkopírují a funkci se předají právě kopie. Tím pádem jakékoli
změny, které funkce provede, že ve skutečnosti provedou na kopiích, které
se při návratu zahazují. Další možností je předávání odkazem. V tomto
případě se funkci předají odkazy na parametry, takže se veškeré změny do
hodnot parametrů promítnou. U parametrů aritm a
geom potřebujeme tedy
předávání odkazem, to se udává operátorem & (nazývá se operátor reference)
u těchto dvou parametrů:
Přetěžování funkcíFunkce min, kterou jsme si napsali, je poměrně užitečná a možná se Vám
bude hodit i v dalším programování. Má ale jednu nevýhodu: umí počítat
nejmenší hodnotu jen pro celá čísla. Chceme-li funkce
min i pro datový typ
double, char atd., musíme si je napsat. Ve starém C muselo jméno funkce
být unikátní, z čehož vyplývá, že např. funkce min pro
double by se
vlastně nemohla jmenovat min, ale třeba min_double. C++ je vyspělejší a
tak v něm existuje mechanismus, který umožňuje deklarovat více funkcí se
stejným jménem - je to přetěžování funkcí (angl. function overloading),
například:
Spustíte-li tento program, vypíše se nejdřív 1, pak 1.5. Kompilátor
poznal jakou funkci chceme volat podle typů parametrů. Pravě takhle to
funguje - dvě funkce se stejným jménem se musí lišit alespoň typem jednoho
parametru. Není možné přetěžovat funkce pouze na základě rozdílného typu
návratové hodnoty. Představte si tento příklad:
Když voláte funkci min jako proceduru, kompilátor by nevěděl, kterou funkci zavolat: tu bez parametru, nebo tu vracející int. Inline funkceKaždé volání funkce spotřebuje nějaký čas procesoru. Není to moc, ale
voláte-li funkci často (například v nějakém cyklu) a záleží-li na každém
taktu, může to být poznat. Právě u takových funkcí může být výhodné je
definovat jako inline. To znamená, že funkce se nezavolá, ale celé její
tělo se vloží na místo, odkud ji voláme (inline
funkce fungují prakticky stejně jako makra). Režie spojená se zavoláním
odpadne a program tak poběží rychleji:
Pozor, funkci deklaruje jako inline pouze tehdy, když máte jistotu, že to potřebujete. V převážné většině případů to potřebovat nebudete, a program se nezrychlí, ale bude vetší. PříkladNa závěr tohoto dílu si ukážeme rozsáhlejší příklad. Bude se jednat o
program počítající, kolika způsoby je možno vybrat ze skupiny n prvků
skupinu k prkvů, bez opakování (počet k-členných variací bez opakování z n
prvků). Například je-li n = 4 a k = 2 jedná se o počet možných výběrů dvou
prvků ze ctyř, přičemž záleží na pořadí - (a,b) je něco jiného než (b,a):
Vybaveni těmito znalostmi se můžeme pustit do práce: nejdřív budeme
potřebovat přečíst parametry z příkazové řádky a připravit je pro výpočet:
Na konci programu vypisujeme výsledek funkcí variace, která spočte
požadovaný počet variací na základě parametrů n a k. Tuto funkci si musíme
definovat (před funkci main, abychom nemuseli psát i deklaraci), podle
matematického vzorce:
Ve funkci variace používáme funkci faktorial, takže ji přidáme:
A to je všechno. Zajímavé je snad jen to, že funkce variace a faktoriál dostávají parametry typu unsigned int (celé číslo bez znaménka) a také takovou hodnotu vracejí. To je v pořádku, variaci ani faktoriál ze záporných čísel počítat nelze. Ale při čtení parametrů z příkazové řádky ve funkci main používáme proměnné typu int, abychom mohli zachytit pokus uživatele o zadání záporných čísel (kdybychom místo int použili i tady unsigned int došlo by při zadání záporného čísla k přetečení a např. místo čísla -1 by se do proměnné uložilo 4294967295).
Děkuji za pozornost a těším se na další díl. |
|
© 2001
Vogel
Publishing, design by
ET NETERA