LINUXSOFT.cz Přeskoč levou lištu
Uživatel: Heslo:  
   CZUKPL

> C/C++ (35) - Reference, funkce

Ukážeme si, co to jsou reference a jak se liší od ukazatelů. V druhé části dílu probereme dvě metody, které umožňují volat funkce se stejným identifikátorem, ale s různou sadou parametrů.

16.2.2006 08:00 | Jan Němec | Články autora | přečteno 32347×

Reference

Jazyk C zná běžné typy a ukazatele na ně. Parametry předávané odkazem, běžně užívané například v Pascalu, v C neexistují. Pokud chceme předat do funkce jako parametr proměnnou a tuto proměnnou (nikoli její kopii na zásobníku) ve funkci modifikovat, musíme použít ukazatel. To s sebou přináší určitá úskalí, neboť je jen na zodpovědnosti programátora ohlídat si, aby byl ukazatel inicializovaný, navíc práce s běžnou proměnnou je díky odlišné syntaxi přeci jen o něco pohodlnější. C++ se snaží problém řešit pomocí referencí. Referenci je nejjednodušší si představit jako neměnný inicializovaný ukazatel se syntaxí běžné proměnné. Lze ji používat jako každý jiný typ při definici proměnných a parametrů i návratové hodnoty funkcí. Reference se definuje stejně jako ukazatel, jen místo znaku * napíšeme &. Nejjednodušší příklad je asi prohození hodnot dvou proměnných.

#include <stdio.h>

// Nefunkční řešení - programátor si neuvědomil, že se vytvářejí kopie
// proměnných. Původní proměnné zůstanou nezměněné.

void prohodNefunguje(int a, int b) {
   int c = a;
   a = b;
   b = c;
}

// Řešení ve stylu C++ (v C neprojde překladem) s použitím referencí
void prohodCPlusPlus(int &a, int &b) {
   int c = a;
   a = b;
   b = c;
}

// Řešení ve stylu C (lze použít i v C++) s použitím ukazatelů
void prohodC(int *a, int *b) {
   int c = *a;
   *a = *b;
   *b = c;
}

int main(void) {
  int x = 1;
  int y = 0;

  printf("x = %i y = %i\n", x, y);
  
  prohodNefunguje(x, y);
  printf("x = %i y = %i\n", x, y);

  prohodCPlusPlus(x, y);
  printf("x = %i y = %i\n", x, y);

  prohodC(&x, &y);
  printf("x = %i y = %i\n", x, y);

  return 0;
}

Jak použití referencí, tak i ukazatelů má v tomto případě své výhody i nevýhody. U ukazatele nemá programátor nikdy jistotu, že je správně inicializovaný, a ani test na NULL neodhalí všechny chyby. Rovněž zápis implementace funkce je trochu krkolomnější. V případě referencí sice klesá (nikoli na nulu!) riziko paměťových chyb, ale programátor zase nepozná na první pohled v místě volání, zda funkce může své parametry modifikovat. V duchu jazyka C++ je v tomto případě užití referencí.

Občas se také hodí reference jako proměnná, například pokud budeme opakovaně přistupovat do pole na stejný index.

int pole[20];

// definice s inicializací
int &pole5 = pole[5];

// totéž jako pole[5] = 1;
pole5 = 1;

Důležité je, že reference musí být inicializovaná již v místě definice a není možné později znovu určit nebo změnit, na kterou proměnnou reference odkazuje. To je podstatná změno oproti ukazatelům, která činí používání referencí o něco bezpečnější.

int pole[20];

// definice bez inicializace - u reference nelze
int *pole5;

// (pravděpodobně) paměťová chyba, u reference nelze tak snadno
*pole5 = 0;

// inicializace
pole5 = pole + 5;

// totéž jako pole[5] = 1;
*pole5 = 1;

// změna ukazatele, u reference nelze
pole5++;

// totéž jako pole[6] = 2;
*pole5 = 2;

Referenci lze použít také jako návratovou hodnotu funkce. V implementaci funkce vrátíme nějakou l-hodnotu, která zůstane platná i po opuštění těla funkce, ne tedy například lokální proměnnou. Ve volajícím kódu lze návratovou hodnotu použít jako běžnou l-hodnotu, takže výsledek funkce lze použít i na levé straně přiřazení.

int data[10];

int & vektor(int index) {
  // Tady můžeme ošetřit meze polí.
  return data[index];
}

int main(void) {
  vektor(5) = 7;
  vektor(3) = vektor(5);
  return 0;
}

Kdybychom se pokusili ve funkci vektor vrátit například číselnou konstantu, došlo by k chybě při překladu funkce vektor. Stejně tak by selhal překlad main, kdybychom návratový typ funkce vektor deklarovali jako běžný int a nikoli referenci.

Je zřejmé, že reference jako návratová hodnota umožní programátorovi implementovat s poměrně jednoduchou syntaxí například bezpečné pole s hlídáním mezí nebo automatickou alokací a podobně. Standardní knihovna C++ skutečně takové nástroje nabízí.

Reference mají oproti ukazatelům ještě některá omezení, která jsme dosud nezmínili. Především nelze definovat ukazatel ani referenci na referenci, zatímco ukazatel na ukazatel se běžně používá a má rozumný smysl. Rovněž nelze definovat referenci na void ani pole referencí.

Z pohledu konzervativního céčkaře přemýšlejícího na úrovni polidštěného asembleru nejspíš reference zůstane bude jen zakukleným ukazatelem s omezenými možnostmi použití, který jen ztěžuje čitelnost programu. Pohled objektově orientovaného programátora, fanouška C++ a především jeho standardní knihovny, bude nejspíš zcela opačný.

Přetěžování funkcí

C++ umožňuje definovat více funkcí se stejným jménem, pokud se odlišují svými parametry. V místě volání překladač automaticky vybere funkci, která s použitím případných implicitních konverzí vyhovuje sadě parametrů. Je-li takových funkcí více, vybere tu, kde jsou konverze nejjednodušší. Teprve pokud ani zde není funkce určená jednoznačně, dojde k chybě. Norma C++ výběr popisuje poměrně striktně, nicméně spíše než detailní studium normy bych doporučil psát kód tak, aby výběr funkce nezávisel například na tom, zda je nějaký parametr typu const char * nebo char *.

#include <stdio.h>

void vypis(int i) {
  printf("%i\n", i);
}

void vypis(const char *s) {
  puts(s);
}

int main(void) {
  vypis(1);
  vypis("text");
  return 0;
}

Implicitní hodnoty parametrů

V C++ je možné definovat implicitní hodnotu jednoho nebo několika posledních parametrů funkce. Pokud nejdou uvedeny, překladač je automaticky doplní na implicitní hodnotu.

#include <stdio.h>
#include <math.h>

double logaritmus(double x, double zaklad = 10) {
  return log10(x) / log10(zaklad);
}

void vypis(const char *s, FILE *f = stdout) {
  fputs(s, f);
}


int main(void) {
  char s[64];

  sprintf(s, "%f, %f\n", logaritmus(100), logaritmus(100, 100));
  vypis(s);
  vypis(s, stderr);
  return 0;
}

Pokud má funkce uvedenou hlavičku v .h souboru, uvádí se hodnota implicitního parametru pouze tam.

// soubor.h
// ...
double logaritmus(double x, double zaklad = 10);
// ...

Implementace je potom již stejná, jako kdyby byly oba parametry povinné.

// soubor.cpp
// ...
double logaritmus(double x, double zaklad) {
  return log10(x) / log10(zaklad);
}
// ...

Je to logické, neboť implicitní parametr se je implementován na úrovni volaného kódu.

Nepoužité parametry

V matematice, stejně jako v C++ lze a občas i má rozumný smysl definovat funkci, která na nějakém svém parametru nezávisí. V obou případech je dobré zdůraznit, že opravdu víme, co děláme a že ten nevyužitý parametr opravdu potřebujeme. Pokud to neuděláme, hrozí v matematice zmatení kolegů a v C++ varování překladače o zbytečném parametru. V C++ stačí neuvést jméno parametru.

void funkce(int) {
}

Nevyužitý parametr má smysl například pokud potřebujeme rozlišit přetíženou funkci nebo pokud předáváme nějaké knihovně ukazatel na naší funkci zpětného volání, která má (pro naše potřeby) zbytečně obecný prototyp.

Domácí úkoly

  • Napište nějaký kód, kde přístup na referenci způsobí nekorektní přístup do paměti.
  • S pomocí implicitních parametrů a přetížených funkcí napište kód, kde si nebudete (až do experimentálního ověření nebo detailního studia normy C++) jistí, jaká funkce se vlastně zavolá. Zkuste to ještě zkomplikovat funkcemi s proměnným počtem parametrů ve stylu C. V praxi takhle neprogramujte.

Pokračování příště

Příště se podíváme na prostory jmen.

Verze pro tisk

pridej.cz

 

DISKUZE

konzervativni &quot;Ceckar&quot; 21.6.2006 11:54 camlost
Proměnná? 18.10.2006 11:42 Petr Kubizňák
  L Re: Proměnná? 19.10.2006 16:45 Jan Němec




Příspívat do diskuze mohou pouze registrovaní uživatelé.
> Vyhledávání software
> Vyhledávání článků

14.11.2017 16:56 /František Kučera

Máš rád svobodný software a hardware nebo se o nich chceš něco dozvědět? Zajímá tě DIY, CNC, SDR nebo morseovka? Přijď na sraz spolku OpenAlt – tradičně první čtvrtek před třetím pátkem v měsíci: 16. listopadu od 18:00 v Radegastovně Perón (Stroupežnického 20, Praha 5).


Přidat komentář

12.11.2017 11:06 /Redakce Linuxsoft.cz
PR: 4. ročník odborné IT konference na téma Datová centra pro business proběhne již ve čtvrtek 23. listopadu 2017 v konferenčním centru Vavruška, v paláci Charitas, Karlovo náměstí 5, Praha 2 (u metra Karlovo náměstí) od 9:00. Konference o návrhu, budování, správě a efektivním využívání datových center nabídne odpovědi na aktuální a často řešené otázky, např Jaké jsou aktuální trendy v oblasti datových center a jak je využít pro vlastní prospěch? Jak zajistit pro firmu či jinou organizaci odpovídající služby datových center? Podle jakých kritérií vybrat dodavatele služeb? Jak volit součásti infrastruktury při budování či rozšiřování vlastního datového centra? Jak efektivně spravovat datové centrum? Jak eliminovat možná rizika? apod.
Přidat komentář

13.9.2017 8:00 /František Kučera
Máš rád svobodný software a hardware nebo se o nich chceš něco dozvědět? Zajímá tě DIY, CNC, SDR nebo morseovka? Přijď na sraz spolku OpenAlt – tentokrát netradičně v pondělí: 18. září od 18:00 v Radegastovně Perón (Stroupežnického 20, Praha 5).
Přidat komentář

3.9.2017 20:45 /Redakce Linuxsoft.cz
PR: Dne 21. září 2017 proběhne v Praze konference "Mobilní řešení pro business". Hlavní tématy konference budou: nejnovější trendy v oblasti mobilních řešení pro firmy, efektivní využití mobilních zařízení, bezpečnostní rizika a řešení pro jejich omezení, správa mobilních zařízení ve firmách a další.
Přidat komentář

15.5.2017 23:50 /František Kučera
Máš rád svobodný software a hardware nebo se o nich chceš něco dozvědět? Zajímá tě DIY, CNC, SDR nebo morseovka? Přijď na sraz spolku OpenAlt, který se bude konat ve čtvrtek 18. května od 18:00 v Radegastovně Perón (Stroupežnického 20, Praha 5).
Přidat komentář

12.5.2017 16:42 /Honza Javorek
PyCon CZ, česká konference o programovacím jazyce Python, se po dvou úspěšných ročnících v Brně bude letos konat v Praze, a to 8. až 10. června. Na konferenci letos zavítá např. i Armin Ronacher, známý především jako autor frameworku Flask, šablon Jinja2/Twig, a dalších projektů. Těšit se můžete na přednášky o datové analytice, tvorbě webu, testování, tvorbě API, učení a mentorování programování, přednášky o rozvoji komunity, o použití Pythonu ve vědě nebo k ovládání nejrůznějších zařízení (MicroPython). Na vlastní prsty si můžete na workshopech vyzkoušet postavit Pythonem ovládaného robota, naučit se učit šestileté děti programovat, efektivně testovat nebo si v Pythonu pohrát s kartografickým materiálem. Kupujte lístky, dokud jsou.
Přidat komentář

2.5.2017 9:20 /Eva Rázgová
Putovní konference československé Drupal komunity "DrupalCamp Československo" se tentokrát koná 27. 5.2017 na VUT FIT v Brně. Můžete načerpat a vyměnit si zkušenosti z oblasti Drupalu 7 a 8, UX, SEO, managementu týmového vývoje, využití Dockeru pro Drupal a dalších. Vítáni jsou nováčci i experti. Akci pořádají Slovenská Drupal Asociácia a česká Asociace pro Drupal. Registrace na webu .
Přidat komentář

1.5.2017 20:31 /Pavel `Goldenfish' Kysilka
PR: 25.5.2017 proběhne v Praze konference na téma Firemní informační systémy. Hlavními tématy jsou: Informační systémy s vlastní inteligencí, efektivní práce s dokumenty, mobilní přístup k datům nebo využívání cloudu.
Přidat komentář

   Více ...   Přidat zprávičku

> Poslední diskuze

18.9.2017 14:37 / Rojas
high security vault

15.9.2017 7:33 / Wilson
new zealand childcare jobs

31.8.2017 12:11 / Jaromir Obr
Re: ukůládání dat ze souboru

30.7.2017 11:12 / Jaromir Obr
Národní znaky

27.7.2017 12:24 / Jaromir Obr
Cteni/zapis

Více ...

ISSN 1801-3805 | Provozovatel: Pavel Kysilka, IČ: 72868490 (2003-2017) | mail at linuxsoft dot cz | Design: www.megadesign.cz | Textová verze