Funkcie

Lokálne a globálne premenné

Lokálne premenné sme používali doteraz:
void __fastcall TForm1::Button1Click(TObject *Sender)
{
int Pocet=0;
Pocet++;
Button1->Caption=Pocet;
}

Premenná Pocet je deklarovaná v rámci tela funkcie. Taká premenná:

* vznikne pri spustení funkcie a zanikne pri skončení funkcie - má lokálnu existenciu
* je "viditeľná" od miesta deklarácie, ale iba pre príkazy funkcie, v ktorej bola deklarovaná (príkazy inej funkcie ju nevidia) - má lokálnu viditeľnosť

Takáto premenná sa nazýva lokálna premenná.

Premenné sa dajú deklarovať aj mimo tela funkcie:
int Pocet=0;

void __fastcall TForm1::Button1Click(TObject *Sender)
{
Pocet++;
Button1->Caption=Pocet;
}

Premenná Pocet je globálna premenná:

* vznikne pri spustení programu, zanikne pri skončení programu
* je viditeľná od miesta deklarácie

Pozor na globálne a lokálne premenné, ktoré majú rovnaké mená:
int Pocet=0;

void __fastcall TForm1::Button1Click(TObject *Sender)
{
int Pocet=0;
Pocet++;
Button1->Caption=Pocet;
}

Pri vykonávaní funkcie:

* v pamäti počítača existujú dve premenné Pocet (lokálna a globálna premenná)
* príkazy funkcie však vidia (používajú) iba lokálnu premennú Pocet - hovoríme, že lokálna premenná prekryla globálnu premennú.


Grafická plocha ako globálna premenná

Zadeklarujeme globálne premenné g, Sirka, Vyska. Tieto premenné musíme inicializovať vo funkcii TForm1::TForm1, ktorá sa, zjednodušene povedané, vykoná po spustení programu:
TCanvas *g;
int Sirka, Vyska;

__fastcall TForm1::TForm1(TComponent* Owner)
: TForm(Owner)
{
g=Image1->Canvas;
Sirka=Image1->Width;
Vyska=Image1->Height;
}

void __fastcall TForm1::Button2Click(TObject *Sender)
{
g->Rectangle(0, 0, 50, 50);
}

void __fastcall TForm1::Button3Click(TObject *Sender)
{
g->Ellipse(0, 0, 50, 50);
}

V programoch sa vo všeobecnosti vyhýbame globálnym premenným a snažíme sa, čo možno najviac, používať lokálne premenné.


Definovanie vlastných funkcií

Čo už vieme:

* program v C++ sa skladá z niekoľkých (väčšinou veľmi veľa) funkcií
* telo každej funkcie obsahuje príkazy, ktoré sa postupne vykonávajú
* niektoré funkcie sme používali (StrToInt, random ... sú to funkcie, aj keď sme im doteraz hovorili všeobecnejšie - príkazy)
* programovali sme telá funkcií, ktoré sa vykonávali pri kliknutí na tlačidlo.

Definovanie funkcie bez parametrov a návratovej hodnoty:

void MenoFunkcie()
{
telo_funkcie
}

* hlavička funkcie:
o vyhradené slovo void znamená, že funkcia iba vykoná príkazy
o meno funkcie je identifikátor (platia preň rovnaké pravidlá, ako pre pomenovanie premenných)
o do zátvoriek () uvádzame parametre (funkcia zatiaľ nemá žiadne parametre, preto sú zátvorky prázdne)
* za hlavičkou funkcie nasleduje telo funkcie - príkazy sú uzavreté v zložených zátvorkách { }

Vytvoríme vlastnú funkciu NahodnyKruzok:
void NahodnyKruzok() // hlavička funkcie
{
int x, y; // telo funkcie
x=random(Sirka);
y=random(Vyska);
g->Ellipse(x-10, y-10, x+10, y+10);
}

void __fastcall TForm1::Button4Click(TObject *Sender)
{
NahodnyKruzok(); // volanie funkcie
}

void __fastcall TForm1::Button5Click(TObject *Sender)
{
int i;
for (i=1; i<=100; i++)
NahodnyKruzok(); // volanie funkcie
}

Pripomeňme, že random(n):

* vygeneruje celé náhodné číslo z rozsahu <0, n-1>
* napr.: random(4) vygeneruje niektoré z čísel 0, 1, 2 alebo 3

Funkcia sa vykoná (presnejšie, príkazy z tela funkcie sa vykonajú), až v keď funkciu "zavoláme" - vykonáme príkaz volania funkcie:

* na mieste v programe, kde sa má funkcia vykonať, napíšeme meno funkcie a zaň uvedieme zátvorky: NahodnyKruzok();
* pri volaní funkcie nesmieme zabudnúť na (), lebo zápis: NahodnyKruzok; bez zátvoriek nič nevykoná (v C++ má tento zápis iný, špeciálny význam)
* v predchádzajúcom príklade sa volanie funkciu NahodnyKruzok vyskytuje na dvoch miestach programu

Príklad: Funkcia na zmazanie grafickej plochy:
void ZmazPlochu()
{
g->Brush->Color=clWhite;
g->Pen->Color=clWhite;
g->Rectangle(0, 0, Sirka, Vyska);
}

Poznámka: Vo vlastných funkciách nemôžeme inicializovať grafickú týmto spôsobom:
TCanvas * g=Image1->Canvas;
g->Rectangle(0, 0, Image1->Width, Image1->Height);
Je to preto, že komponenty formulára nie sú vo vlastných funkciách priamo viditeľné. Museli by sme písať:
TCanvas * g=Form1->Image1->Canvas;
g->Rectangle(0, 0, Image1->Width, Image1->Height);
My radšej budeme používať globálne premenné g, Sirka, Vyska.


Funkcie s jednoduchými parametrami

Teraz nechceme kresliť náhodne umiestnený krúžok. Chceme nakresliť krúžok, ktorý leží na určitom mieste - napríklad, jeho stred má súradnice x, y. Treba zabezpečiť, aby sa funkcia "dozvedela" tieto súradnice. Na to požívame parametre funkcie:
void Kruzok(int x, int y)
{
g->Ellipse(x-10, y-10, x+10, y+10);
}

void __fastcall TForm1::Button6Click(TObject *Sender)
{
Kruzok(100, 100); // volanie funkcie s parametrami
Kruzok(200, 100);
}

Hlavička funkcie obsahuje zoznam parametrov:

* parametre deklarujeme v tvare: typ_parametra názov_parametra
* ak má funkcia viacero parametrov, oddeľujeme ich čiarkami: typ1 nazov1, typ2 nazov2 ....

Parametre funkcie majú podobné vlastnosti ako lokálne premenné:

* sú viditeľné pre príkazy z tela funkcie
* vzniknú pri zavolaní funkcie, zaniknú pri skončení funkcie
* navyše, do parametrov sa priradia hodnoty, ktoré sme uviedli v zátvorkách pri volaní funkcie
Kruzok(100, 100); // parameter x==100 a y==100
Kruzok(200, 100); // parameter x==200 a y==100

Príklad: Funkcia Usecka:
void Usecka(int x1, int y1, int x2, int y2)
{
g->MoveTo(x1, y1);
g->LineTo(x2, y2);
}

void __fastcall TForm1::Button7Click(TObject *Sender)
{
Usecka(10, 10, 100, 50);
}

void __fastcall TForm1::Button8Click(TObject *Sender)
{
int i;
for (i=0; i<=300; i=i+5)
Usecka(0, i, 500, 300-i); // volanie funkcie s meniacimi sa parametrami
}

Pri volaní funkcie sa môžu nachádzať na miestach parametrov nielen konštanty, ale aj výrazy. Tie sa pred zavolaním funkcie vyhodnotia.


Funkcie s návratovou hodnotou

Predchádzajúce funkcie sú tak trochu zvláštne, pretože nedávajú žiaden výsledok - nemajú žiadnu výslednú hodnotu.
int Cislo()
{
return 10*4; // vráť výsledok 40
}

void __fastcall TForm1::Button9Click(TObject *Sender)
{
int n;
n=Cislo(); // výsledok uložíme do premennej n
g->TextOut(0, 0, n); // vypíše sa 40
}

Hlavička funkcie obsahuje namiesto slova void vyhradené slovo int. Tým je povedané, že výsledok funkcie je celé číslo (takáto funkcia nielenže vykonáva príkazy, ale "dáva" celočíselný výsledok).

Príkaz return hodnota;:

* týmto príkazom určíme výsledok funkcie - tzv. návratovú hodnotu
* v prípade, že sa za vyhradeným slovom return nachádza výraz, výraz sa najskôr vyhodnotí a jeho hodnota sa použije ako výsledok funkcie
* príkaz return zároveň ukončí vykonávanie funkcie (ak by za príkazom return nasledovali ďalšie príkazy, nevykonajú sa)

Príklad: Z matematiky poznáme definíciu mocniny: x->x2. To isté môžeme v C++ zapísať nasledujúcim spôsobom:
float Mocnia(float x)
{
return x*x; // vráť výsledok x2
}

void __fastcall TForm1::Button10Click(TObject *Sender)
{
g->TextOut(0, 20, Mocnina(10)); // výsledok vypíšeme priamo do grafickej plochy
}

Nasledujúca funkcia vypočíta súčet 1+2+...+n:
int Sucet(int n)
{
int i, s;
s=0;
for (i=1; i<=n; i++) s=s+i;
return s; // výsledok - hodnota v premennej s
}

void __fastcall TForm1::Button7Click(TObject *Sender)
{
g->TextOut(0, 40, Sucet(10));
g->TextOut(0, 60, Sucet(20));
}



Knižnice funkcií

V programoch môžeme používať rôzne matematické funkcie, ktoré už niekto naprogramoval a zabalil to tzv. knižníc. Matematické funkcie, napr., sin, cos, sqrt (sínus, kosínus, odmocnina) a ďalšie sa nachádzajú v matematickej knižnici s názvom math. Aby sme príkazy mohli používať, treba na začiatok programu vložiť informáciu pre kompilátor o tom, ktoré knižnice program potrebuje:
#include <vcl.h>
#pragma hdrstop

#include <math.h>
#include "Unit1.h"

Teraz môžeme použiť funkciu sin:
g->TextOut(0, 60, sin(1)); // sínus 1 radiánu


Farby pomocou RGB

Poznáme typ TColor a niekoľko základných farieb: clBlack, clWhite, clRed... Ak chceme používať iné farby musíme ich "namiešať". V počítačoch sa farby miešajú pomocou svetiel Red, Green, Blue - ich vzájomná intenzita určuje výslednú farby. Intenzita každého svetla sa udáva celým číslom v rozsahu <0, 255>. K dispozícií tak máme 2563 možných kombinácií, ktoré sú očíslované.

Funkcia RGB(r, g, b) vráti číslo farby, ktorá vznikne zmiešaním červeného, zeleného a modrého svetla:

RGB(0, 0, 0) ... číslo pre čiernu farbu
RGB(255, 0, 0) ... číslo pre červenú farbu
RGB(255, 0, 255) ... číslo pre fialovú farbu

V programoch pri nastavovaní farby nepoužívame čísla ale hodnoty typu TColor - z "čísla farby" treba vyrobiť "farbu":
TColor(RGB(255, 0, 0)).

Príklad použitia:
void __fastcall TForm1::Button8Click(TObject *Sender)
{
int i;
for (i=0; i<=255; i++) {
g->Pen->Color=TColor(RGB(i, i, 0));
Usecka(i, 0, i, 100);
}
}


Zhrnutie

Písanie funkcií je veľmi elegantný a efektívny spôsob vytvárania programov. Ak chceme používať rovnaký algoritmus na viacerých miestach programu, vytvoríme funkciu, v ktorej algoritmus raz naprogramuje. Potom funkciu voláme na miestach, kde sa má algoritmus vykonať.

Definovanie funkcie:

typ meno(parametre) {
telo_funkcie
}

* typ ... typ výsledku - aké hodnoty funkcia vracia (void, int, float ...)
* meno ... meno funkcie
* parametre ... zoznam parametrov oddelených čiarkami
* telo_funkcie ... príkazy

Ak funkcia vracia výsledok (ak typ výsledku nie je void), musíme v tele funkcie vykonať príkaz:

return výraz;

Príkaz return ukončí vykonávanie funkcie, pričom výsledkom funkcie bude hodnota výrazu.