Implicitní parametry funkcí a polymorfismus

C++ Objektově orientované programování Implicitní parametry funkcí a polymorfismus

V tomto tutoriálu vysvětlím dva velmi užitečné a často používané mechanismy jazyka C++. Pro implicitní parametry funkcí můžeme najít uplatnění teoreticky kdekoliv, kde za nějaký parametr funkce často dosazujeme stejnou hodnotu. Polymorfismus se používá spíše v souvislosti s třídami, může ovšem najít uplatnění v klasických funkcích.

Implicitní parametry funkcí

Určitě se vám čas od času stane, že nějaký parametr ve vámi vytvořené funkci nabývá té samé hodnoty ve většině případů, ve kterých funkci voláte. Řekněme, že vytvoříme funkci pro získání náhodné hodnoty v určitém intervalu. Naše funkce bude vypadat třeba takto:

int GetRandomNumber(int min, int max)
{
  return rand() % (max - min + 1) + min;  //funkce vrátí náhodné číslo od min do max
}

Co když ale v našem programu budeme téměř vždy potřebovat náhodné číslo od 1 do 100 a pouze výjimečně hodnotu z jiného intervalu. Bylo by pak praktické, kdybychom nemuseli vždy při volání funkce zadávat parametry 1 a 100, ale zároveň bychom měli možnost zadat parametry jiné. Toho můžeme snadno dosáhnout pomocí takzvaných implicitních parametrů. Implicitní parametr určíme tak, že mu v deklaraci přiřadíme nějakou hodnotu operátorem =. Naše funkce s implicitními parametry min = 1 a max = 100 by vypadala takto:

int GetRandomNumber(int min = 1, int max = 100)
{
  return rand() % (max - min + 1) + min;
}

Pokud teď tuto funkci zavoláme a nepředáme jí žádné parametry, kompilátor automaticky dosadí za min 1 a za max 100. Pokud parametry předáme, funkce tyto parametry použije. Pokud předáme pouze jeden parametr, funkce ho dosadí za min a max zůstane 100. Ovšem neexistuje způsob, jak změnit pouze max, aniž bychom museli zadat i min.

GetRandomNumber();  //vrátí náhodné číslo od 1 do 100
GetRandomNumber(1,100);  //to samé
GetRandomNumber(10,20);  //vrátí náhodné číslo od 10 do 20
GetRandomNumber(50);  //vrátí náhodné číslo od 50 do 100 (za min se dosadí 50, max zůstane 100)

Implicitní parametry můžeme samozřejmě míchat s klasickými parametry, ovšem implicitní parametry musí vždy být na konci. Například toto není možné:

void function(int a, int b = 50, int c);  //chyba, za implicitním parametrem následuje klasický parametr

Přidejme naší funkci GetRandomNumber ještě jeden parametr, a to seed.

int GetRandomNumber(int seed, int min = 1, int max = 100)  //parametr seed musí být na začátku
{
  srand(seed);
  return rand() % (max - min + 1) + min;
}

Parametr seed musíme zadat vždy, pro další dva parametry platí ta samá pravidla jako doposud.

GetRandomNumber(15);  //vrátí náhodné číslo od 1 do 100
GetRandomNumber(15,1,100);  //to samé
GetRandomNumber(15,10,20);  //vrátí náhodné číslo od 10 do 20
GetRandomNumber(15,50);  //vrátí náhodné číslo od 50 do 100 (za min se dosadí 50, max zůstane 100)

Pokud rozdělíte funkci na deklaraci a definici, musíte implicitní parametry definovat při deklaraci, při definici funkce pak již nemusíte.

int GetRandomNumber(int, int min = 1, int max = 100);  //u klasických parametrů stačí v deklaraci uvést pouze typ, implicitní parametry je nutno uvést i s názvem a implicitní hodnotou
...
int GetRandomNumber(int seed, int min, int max)  //při definici již není potřeba uvádět implicitní hodnoty
{
  srand(seed);
  return rand() % (max - min + 1) + min;
}

Přetěžování funkcí (polymorfismus)

Přetěžování funkcí je, stejně jako implicitní parametry, prostředek pro usnadnění práce. Využívá se sice především v souvislosti s třídami a objektovým přístupem, má ale uplatnění i u klasických samostatných funkcí. Přetížení funkce znamená, že vytvoříme dvě nebo více funkcí se stejným názvem, ale jinou signaturou. Tedy, každá s těchto funkcí přebírá parametry jiných typů nebo i jiný počet parametrů. Při volání kompilátor podle předaných parametrů pozná, kterou z funkcí má použít. Přetěžování nám tedy v podstatě umožňuje vytvořit několik tvarů funkce se stejným názvem, odtud také pojem polymorfismus, neboli vícetvárnost. Řekněme, že chceme opět vytvořit funkci pro získání náhodného čísla z určitého intervalu. Budeme ovšem chtít jednu funkci pro náhodné číslo celé a jednu pro číslo desetinné. Bylo by dobré, kdyby tyto dvě funkce měly stejný název a podle parametrů kompilátor poznal, zda má vygenerovat celé nebo desetinné náhodné číslo. Můžeme to napsat třeba takto:

int GetRandomNumber(int min, int max)
{
  return rand() % (max - min + 1) + min;  //funkce vrátí náhodné číslo typu int od min do max
}

double GetRandomNumber(double min, double max)
{
  double rnd = rand() % (int)(max * 100 - min * 100 + 1) - min * 100;
  return rnd / 100;  //funkce vrátí náhodné číslo typu double od min do max s přesností na dvě desetinná místa
}

Pokud teď použijeme funkci GetRandomNumber a předáme jí dvě hodnoty typu int, například 1 a 10, zavolá se první funkce a vrátí nám náhodnou hodnotu typu int. Pokud předáme dva parametry typu double, například 4.57 a 12.6, zavolá se druhá funkce a vrátí náhodnou hodnotu typu double. Pokud ale předáme jednu hodnotu typu double a druhou hodnotu typu int, kompilátor zahlásí chybu kvůli nejednoznačnosti.

GetRandomNumber(0,10);  //vrátí náhodnou hodnotu od 0 do 10 typu int
GetRandomNumber(0.0, 10.0);  //to samé ale hodnota bude typu double, protože 0.0 a 10.0 narozdíl od 0 a 10 jsou implicitně typu double
GetRandomNumber(0, 10.0);  //chyba, první hodnota je typu int, druhá typu double, kompilátor neví kterou funkci zavolat

Co když ale použijeme třeba dvě hodnoty typu float nebo char? Kompilátor jednoduše použije tvar, který je signaturou blíže předaným parametrům. Float má blíže k double, takže v tomto případě by kompilátor zvolil druhou funkci, char má blíže k int, takže v této situaci by kompilátor zvolil funkci první.

GetRandomNumber(0.0f, 10.0f);  //vrátí hodnotu typu double
GetRandomNumber('a','z');  //vrátí hodnotu typu int

Pokud bychom chtěli vytvořit pointer na jednu z přetížených funkcí, stačilo by zadat pointeru správnou signaturu, podle které kompilátor již pozná, kterou funkci má použít.

int (*ptr1)(int,int) = GetRandomNumber;  //ptr1 dostane adresu první funkce
double (*ptr2)(double,double) = GetRandomNumber;  //ptr2 dostane adresu druhé funkce

Polymorfismus samozřejmě můžeme zkombinovat s implicitními parametry. Řekněme, že budeme chtít funkci pro získání náhodného desetinného čísla přidat parametr pro počet desetinných míst, který bude implicitně nastaven na hodnotu 2.

int GetRandomNumber(int min, int max)
{
  return rand() % (max - min + 1) + min;  //funkce vrátí náhodné číslo typu int od min do max
}

double GetRandomNumber(double min, double max, int pr = 2)
{
  int mul = 1;
  for(int i = 0; i < pr; i++, mul *= 10);
  double rnd = rand() % (int)(max * mul - min * mul + 1) - min * mul;
  return rnd / mul;  //funkce vrátí náhodné číslo typu double od min do max s přesností na pr desetinných míst
}

Pokud budeme nyní při volání předávat dva parametry, bude vše fungovat úplně stejně jako do teď. Pokud ovšem přidáme třetí parametr, chování se poněkud změní. Především, první funkce se nikdy nezavolá, pak také kompilátor nebude mít problém s nejednoznačností, pokud například za první parametr dosadíme hodnotu typu double a za druhý hodnotu typu int.

GetRandomNumber(0,10);  //vrátí náhodnou hodnotu typu int
GetRandomNumber(0.0, 10.0);  //vrátí náhodnou hodnotu typu double se dvěma desetinnými čísly
GetRandomNumber(0, 10.0);  //chyba, první hodnota je typu int, druhá typu double, kompilátor neví kterou funkci zavolat
GetRandomNumber(0.0, 10.0, 3);  //vrátí náhodnou hodnotu typu double se třemi desetinnými čísly
GetRandomNumber(0, 10.0, 3);  //to samé - pouze jedna funkce s tímto názvem může přebrat tři parametry, proto nedojde k nejednoznačnosti a kompilátor přetypuje 0 na double

 

  Aktivity (1)

Článek pro vás napsal Lukáš Hruda (Luckin)
Avatar
...

Jak se ti líbí článek?
Celkem (3 hlasů) :
55555


 



 

 

Komentáře

Děláme co je v našich silách, aby byly zdejší diskuze co nejkvalitnější. Proto do nich také mohou přispívat pouze registrovaní členové. Pro zapojení do diskuze se přihlas. Pokud ještě nemáš účet, zaregistruj se, je to zdarma.

Zatím nikdo nevložil komentář - buď první!