Vydělávej až 160.000 Kč měsíčně! Akreditované rekvalifikační kurzy s garancí práce od 0 Kč. Více informací.
Hledáme nové posily do ITnetwork týmu. Podívej se na volné pozice a přidej se do nejagilnější firmy na trhu - Více informací.

13. diel - Funkcie v jazyku C ++

V minulej lekcii, Kvíz - textové reťazce v C++ , sme si predstavili matematickú knižnicu cmath. Dnešné tutoriál o programovacom jazyku C ++ je venovaný veľmi dôležitej téme, ktorým je využívanie funkcií. My sme už zoznámenie s tým, že kód programu píšeme do funkcie main(). To pre naše učebné programy, ktoré vedeli vykonávať len jednu jednoduchú vec, zatiaľ stačilo. Predstavte si však, že píšete program, ktorý je dlhý niekoľko sto tisíc riadkov. Určite uznáte, že v takej rezance kóde v jednom súbore a v jednej funkcii by sa orientovalo veľmi zle. Navyše, ak by sme chceli vykonať nejakú rovnakú postupnosť príkazov na viacerých miestach, museli by sme ju buď stále opisovať alebo v kóde skákať z miesta na miesto. Obe dve možnosti sú opäť veľmi neprehľadné.

Funkcionálne dekompozícia

O rozdelení aplikácie do funkcií sa niekedy hovorí ako o tzv. Funkcionálne dekompozícii. Neľakajte sa termíne, jednoducho si rozmyslíme, čo má naša aplikácia vedieť, a pre rôzne užívateľské funkcie zvyčajne vytvoríme jednotlivé funkcie v zdrojovom kóde. V praxi sa nám bude často stávať, že si budeme tvoriť okrem týchto funkcií ešte nejaké pomocné, napr. Môžeme mať funkciu pre výpis menu aplikácie alebo rozdelíme nejaký zložitý výpočet do viacerých menších funkcií kvôli prehľadnosti.

Funkciám sa niekedy hovorí podprogramy alebo subrutiny. Ak funkcia nevracia žiadnu hodnotu (viď ďalej), môže sa ju v niektorých jazykoch hovoriť procedúra. U väčších aplikácií, ktoré majú mnoho funkcií, sa funkcia združujú do tzv. Modulov. Tie vy dobre poznáte napr. V podobe #include <iostream>, ktorým načítame knižnicu (modul) pre prácu so štandardným vstupom a výstupom (teda pre nás s konzolou). Podobne sú matematické funkcie sústredené v systémovom module cmath. Tieto moduly alebo ak knižnice sa tiež naučíme vytvárať.

Tvorba funkcií

Funkcia je logický blok kódu, ktorý raz napíšeme a potom ho môžeme ľubovoľne volať bez toho, aby sme ho písali znovu a opakovali sa. Funkciu deklarujeme v globálnom priestore, niekde nad funkciou main(). Bude vyzerať podobne. Pridajme do nášho zdrojového kódu funkciu, ktorá do konzoly vypíše " Ahoj, vrelo ťa tu vítam! ". Pre názornosť si prvýkrát uveďme kompletný zdrojový kód programu:

#include <iostream>
using namespace std;

void pozdrav()
{
    cout << "Ahoj, vrele te tú vitam!" << endl;
}

int main()
{
    return 0;
}

Kľúčové slovo void v definícii funkcie označuje, že funkcia nevracia žiadnu hodnotu. Funkciu teraz musíme zavolať, aby sa spustila. Musíme to samozrejme urobiť až po tom, čo ju deklarujeme, inak by ju kompilátor nepoznal (preto sme ju písali nad funkciu main()). Do main() napíšeme tento riadok:

pozdrav(); // zavolanie funkcie

Do konzoly by sa v tejto chvíli malo vypísať Ahoj, vrele te tú vitam.

Funkcie s parametrami

Funkcia môže mať tiež ľubovoľný počet vstupných parametrov (niekedy sa im hovorí argumenty), ktoré píšeme do zátvorky v jej definícii. Parametre ovplyvňujeme správanie funkcie. Majte situácii, keď chceme pozdraviť nášho používateľa podľa mena. Rozšírime teda existujúce funkciu o parameter jmeno a ten potom pridáme s konkrétnou hodnotou do volanie funkcie:

void pozdrav(string jmeno)
{
    cout << "Ahoj, vrele te tú vitam " << jmeno << endl;
}

Funkciu v main() následne zavoláme takto:

pozdrav("Karle"); // zavolanie funkcie

Keby sme teraz chceli pozdraviť niekoľko ľudí, nemusíme otrocky písať znovu a znovu cout << "Ahoj, vrele..., stačí nám len viackrát zavolať našej funkcii:

pozdrav("Karle");
pozdrav("Davide");
pozdrav("Marenko");

výsledok:

Konzolová aplikácia
Ahoj, vrele te tú vitam Karle
Ahoj, vrele te tú vitam Davide
Ahoj, vrele te tú vitam Marenko

Návratová hodnota funkcie

Funkcia môže ďalej vracať nejakú hodnotu. Opusťme náš príklad s pozdravom a vytvorme tentoraz funkciu, ktorá nám spočíta obsah obdĺžnika. Tento obsah však nebudeme chcieť len vypísať, ale budeme ho chcieť použiť v ďalších výpočtoch. Preto výsledok funkcie nevypíše, ale vrátime ako návratovú hodnotu. Funkcia môže vracať práve jednu hodnotu pomocou príkazu return. Return okrem navrátenie hodnoty funkciu tiež okamžite ukončí, ďalšie prípadné príkazy po ňom sa už nevykonajú. Dátový typ návratovej hodnoty musíme uviesť pred definíciu funkcie. Pridajte si do programu nasledujúcej funkcii:

int obsah_obdelniku(int sirka, int vyska)
{
    int vysledek = sirka * vyska;
    return vysledek;
}

V praxi by naše funkcie samozrejme počítala niečo zložitejšieho, aby sa nám ju vyplatilo vôbec programovať. V príklade ale obdĺžnik poslúži dobre. Funkcia pomenovávame malými písmenami, celými slovami a miesto medzier používame podčiarknutia. Hoci C ++ samotné je plné skrátené, vy sa im vyhnite. Je totiž oveľa čitateľnejšie funkcie datum_narozeni() ako datnar(), u ktorej nemusia byť na prvý pohľad zrejmé čo že to vôbec robí.

Ak by sme teraz chceli vypísať obsah nejakého obdĺžnika, jednoducho vložíme volanie funkcie do volania cout. Ako prvý sa spočíta obsah obdĺžnika, funkcia túto hodnotu vráti a hodnota príde ako vstupný parameter objektu cout, ktorý ju vypíše. Ako šírku a výšku zadajte napr. 10 a 20 cm:

cout << "Obsah obdĺžnikov je: " << obsah_obdelniku(10, 20) << " cm^2" << endl;
Konzolová aplikácia
Obsah obdĺžnikov je 200 cm^2

Pokiaľ vám to príde zmätené, vždy môžete použiť ešte pomocnú premennú:

int obsah = obsah_obdelniku(10, 20);
cout << "Obsah obdĺžnikov je: " << obsah << " cm^2" << endl;

Návratovú hodnotu funkcie sme však nepoužili kvôli tomu, aby sme ju len vypisovali. Využime teraz toho, že je výpis na nás, a výpisy súčet obsahov dvoch obdĺžnikov:

int celkovy_obsah = obsah_obdelniku(10, 20) + obsah_obdelniku(20, 40);
cout << "Sucet obsahu obdĺžnika je: " << celkovy_obsah << " cm^2" << endl;

výsledok:

Konzolová aplikácia
Sucet obsahu obdĺžnikov je: 1000 cm^2

Spomeňme si na minulé príklady, ktoré sme počas nášho seriálu vytvorili. Môžete si ich skúsiť prepísať tak, aby ste volali funkciu. V rámci návrhu by všetok kód mal byť rozdelený do funkcií (a ideálne do modulov, viď. Ďalší lekcie) a to najmä kvôli prehľadnosti. My sme to zo začiatku kvôli jednoduchosť zanedbali, teraz to prosím berte na vedomie :)

Výhoda funkciou je teda v prehľadnosti a úspornosti (môžeme napísať nejakú vec raz a volať ju treba stokrát na rôznych miestach programu). Keď sa rozhodneme funkciu zmeniť, vykonáme zmenu len na jednom mieste a táto zmena sa prejaví všade, čo značne znižuje riziko chýb. V príklade, kde zdravíme Karla, Davida a Marienku, nám stačí zmeniť text pozdravu vo funkcii a zmení sa vo všetkých troch volaniach. Nemať kód vo funkcii, museli by sme prepisovať 3 vety a v nejakej by sme mohli urobiť chybu.

Rekurzia

Na koniec si urobme ešte odbočku k pokročilejšiemu téme, ktorým je rekurzia. Rekurzívne funkcie je taká funkcia, ktorá v tele volá sama seba. Takáto funkcia potrebuje nejakú informáciu, podľa ktorej spozná, kedy má skončiť (tzv. Ukončenie rekurzia), inak by zavolala seba, tá zas seba a tak až do pádu programu na nedostatok pamäti. Rekurzia sa často používa v algoritmizácia.

Vo funkcionálnych jazykoch sa rekurzia používa namiesto cyklov. Zoberme si napríklad cyklus for, ktorý je súčtom čísla od 1 do 10. Rovnaký výsledok môžeme docieliť aj rekurzia, funkcie sa buď zavolá znovu s číslom o 1 vyšším alebo sa ukončí.

int cyklus(int aktualni_index, int konecny_index, int suma)
{
    if (aktualni_index == konecny_index)
        return suma;
    return cyklus(aktualni_index + 1, konecny_index, suma + aktualni_index);
}

Funkciu by sme zavolali takto:

cout << cyklus(0,10,0) << endl; // začiatok rekurzia

To isté môžeme zapísať pomocou cyklu for:

// ekvivalentné zápis s for
int suma = 0;
for (int a = 0; a < 10; a++)
    suma += a;
cout << suma;

Ako môžete vidieť, čítanie rekurzia nie je tak jednoduché, ako je tomu u cykle for. Aby toho nebolo málo, použitie rekurzia sebou nesie dodatočnú záťaž, pretože sa musí opakovane odovzdávať parametre (viac v článku o kompiláciu). Všeobecne možno veľkú časť programov, ktoré používajú rekurziu, prepísať do podoby bez rekurzia. Pre príklad si napíšeme program, ktorý počíta faktoriál. Predvedieme si verziu s rekurzia a verziu bez rekurzia.

int faktorial(int x)
{
        if (x == 1)
                return 1;
        return x * faktorial(x - 1);
}

A funkciu by sme zavolali takto:

cout << faktorial(10) << endl;

A alternatíva pomocou cyklu:

int vysledek = 1;
int x = 10;
for (int i = 2; i <= x; i++)
        vysledek *= i;
cout << vysledek << endl;

S rekurzia sa môžete často stretnúť v už existujúcich programoch alebo na pohovoroch do práce. Avšak odporúčam rekurziu z dôvodov výkonnostných skôr nepoužívať. Rekurzia dokáže veľmi rýchlo zaplniť zásobník a ukončiť celý program. Naviac je zložitá na chápania, ak vás zmiatla, ešte sa s ňou stretnete minimálne u algoritmov, kde bude dostatok priestoru pre ďalšie pochopenie.

V budúcej lekcii, Riešené úlohy k 13. lekcii C ++ , už trochu nazrieme do OOP. Pozrieme sa na štruktúry, ktoré sú takým medzikrokom medzi procedurálnym programovaním a OOP.

V nasledujúcom cvičení, Riešené úlohy k 13. lekcii C ++, si precvičíme nadobudnuté skúsenosti z predchádzajúcich lekcií.


 

Predchádzajúci článok
Kvíz - textové reťazce v C++
Všetky články v sekcii
Základné konštrukcie jazyka C ++
Preskočiť článok
(neodporúčame)
Riešené úlohy k 13. lekcii C ++
Článok pre vás napísal Patrik Valkovič
Avatar
Užívateľské hodnotenie:
Ešte nikto nehodnotil, buď prvý!
Věnuji se programování v C++ a C#. Kromě toho také programuji v PHP (Nette) a JavaScriptu (NodeJS).
Aktivity