Image
16.6.2016 0 Comments

C++ / Čo je nové v C++? / 22. časť

Späť na úvod >> Späť na programovanie >> Späť na seriál

V predposlednej časti seriálu budeme hovoriť o najnovších črtách jazyka C++, o vlastnostiach, ktoré sa stali de iure súčasťou jazyka až nedávno, a to na základe kodifikácie normou ANSI, o používaných i menej používaných prvkoch, jednoducho o všetkom tom, o čom zatiaľ ani mnohí skúsení programátori v C++ nemajú potuchy (dúfam, že tento stav je len dočasný a vďaka tomuto článku sa rýchlo zmení). O niekoľkých črtách, ktoré by sa dali považovať za viac-menej nové, som sa v priebehu seriálu explicitne zmienil. Veci, o ktorých sa dozviete dnes, môžete brať skutočne ako „crème de la crème“ toho, čo sa možno o C++ dozvedieť. Bohužiaľ, neznamená to však, že budete môcť všetky tieto nové vlastnosti aj používať – nijaký dnes dostupný prekladač nie je úplne konformný s normou ANSI, v implementácii každého z nich existujú menšie či väčšie odchýlky od štandardu. Útechou nech vám je opojný pocit, že sa po prečítaní článku budete so svojimi vedomosťami nachádzať, ako sa hovorí, „on the cutting edge“ alebo, po slovensky povedané, na špici súčasného stavu jazyka C++.

Skôr než začneme, chcel by som vás ešte upozorniť, že vývoj jazyka C++ prebiehal a prebieha kontinuálne, mnohé črty sa stali jeho de facto súčasťou dávno predtým, než boli podchytené normou ANSI. Je preto možné, že niektoré tu opisované vlastnosti budú pre niekoho z vás známe, prípadne ste o nich počuli už dávnejšie. V takom prípade ma, prosím, nekameňujte za to, že som si dovolil označiť ich ako „nové“. Pre väčšinu z vás novými budú.

Kľúčové slová

Súčasťou C++ je niekoľko nových kľúčových slov, ktoré môžeme použiť ako ekvivalenty niektorých operátorov. Ich zavedenie sa odôvodňuje potenciálne obťažným zápisom niektorých ASCII znakov, tvoriacich tieto operátory, v určitých znakových sadách. Priznám sa, pokladám to za dosť pochybný dôvod, ale dajme tomu. Nové kľúčové slová spolu s ekvivalentnými operátormi sú v tabuľke 1.

Kľúčové slovo

Operátor

and

and_eq

bitand

bitor

compl

not

not_eq

or

or_eq

xor

xor_eq

&&

&=

&

|

~

!

!=

||

I=

^

^=

Tabuľka č. 1

V niektorých prekladačoch sú tieto kľúčové slová implementované pomocou makier, #definovaných v štandardnom hlavičkovom súbore <iso646.h>.

Údajové typy

Na reprezentáciu znakov, ktorých číselná reprezentácia sa nevojde do rozsahu typu char (typicky znaky ázijských abecied, resp. znaky v kóde Unicode), je k dispozícii údajový typ wchar_t. Jeho šírka je obyčajne 16 bitov, hoci to norma nijako nepredpisuje. Širokoznakové literály môžeme zadávať buď klasickým spôsobom v apostrofoch, ale s predradenou predponou L, alebo pomocou escape sekvencie \uxxxx (znaky Unicode). Príklady:

wchar_t c1 = L'ab';
wchar_t c2 = L'\x0F\x3A';
wchar_t c3 = '\u109D';

Podpora escape sekvencie \uxxxx je však zatiaľ veľmi zriedkavá.

Druhým novým údajovým typom je typ bool, ktorý slúži, ako správne predpokladáte, na reprezentáciu logických hodnôt. Jeho povolený rozsah tvoria dve hodnoty – true a false –, ktoré sú súčasne kľúčovými slovami C++. Nad typom bool môžeme používať bežné logické operátory &&, || a !. Je možné, samozrejme, použiť aj bitové operátory &, |, ^ a ~, ale tie majú za následok celočíselné rozšírenie typu bool na typ int (intuitívne – false sa rozšíri na hodnotu 0 a true na hodnotu 1). Navyše môžeme na premennú typu bool aplikovať prefixový či postfixový operátor ++, ktorý spôsobí, že sa premenná nastaví na hodnotu true. Bohužiaľ, opačný spôsob – aplikácia operátora –– – nie je povolený. Ľubovoľný celočíselný typ môžeme konvertovať na typ bool: výsledkom konverzie nulovej hodnoty je hodnota false, výsledkom konverzie nenulovej hodnoty je, naopak, hodnota true.

So zavedením typu bool sa zmenil aj výsledný typ podmienkového výrazu. Podmienkový výraz je výraz obsahujúci niektorý z operátorov ==, !=, <, <=, >, >=. Teda napríklad výraz i==0 bude mať hodnotu true za predpokladu, že obsah premennej i bude nulový, a hodnotu false v ostatných prípadoch. Podmienkové výrazy sú súčasťou príkazov if, while, do a for, takže napríklad nekonečný cyklus je možné zapísať aj takto:

while (true)
{ /* ... */ }

Operátory pretypovania

Pretypovanie premenných a konštánt v C++ sa považuje za neveľmi bezpečný prvok jazyka, pretože pri jeho nevhodnom použití môže dôjsť k nesprávnej interpretácii údajov, s ktorými program pracuje, a následne k závažným chybám, často vedúcim až k zrúteniu programu.

Klasický spôsob pretypovania sa opiera o operátor pretypovania známy ešte z jazyka C, prípadne o syntax podobnú volaniu funkcie, špecifickú pre C++:

a = (int)b;
a = int(b);

Z dôvodu zvýšenia bezpečnosti bola úloha pretypovacieho operátora rozdelená medzi štyri nové operátory dynamic_cast, static_cast, const_cast a reinterpret_cast. V nasledujúcich odsekoch si ich preberieme podrobnejšie.

Operátor dynamic_cast

Prvý operátor dynamic_cast slúži na dynamické pretypovanie ukazovateľov, resp. referencií na objektové typy. Pretypovanie je možné iba v rámci hierarchie dedičnosti. Vieme, že je bez problémov možné pretypovať ukazovateľ na potomka na ukazovateľ na predka, to isté platí pre referencie. Často však bude potrebné pretypovať ukazovateľ či referenciu opačným smerom, keď celkom určite vieme, že ukazovateľ na predka v skutočnosti ukazuje na inštanciu niektorého jeho potomka. Práve na tieto účely máme k dispozícii operátor dynamic_cast. Jeho syntax je takáto:

dynamic_cast < cieľový typ > ( ukazovateľ )
dynamic_cast < cieľový typ > ( referencia )

Pretypovávaný ukazovateľ, resp. referencia musia odkazovať na inštanciu objektového typu. Cieľový typ musí byť trieda, ktorá je predkom alebo potomkom triedy pretypovávanej inštancie.

Operátor dynamic_cast využíva tzv. dynamickú identifikáciu typu (RTTI – real-time type identification), pomocou ktorej si overí, či je pretypovanie možné a má zmysel. Už z názvu vyplýva, že táto kontrola sa deje za behu programu, a teda je možné pretypovávať iba polymorfné triedy s virtuálnymi metódami.

Mnohorakosť použitia operátora dynamic_cast naznačí nasledujúci príklad: Predstavme si, že máme objekt triedy E, ktorá je súčasťou hierarchie tried, znázornenej na obr. 1. V rámci tejto hierarchie môžeme ukazovateľ na E pretypovať na ukazovateľ na ľubovoľnú inú triedu A až D v smere aj proti smeru šípok vyjadrujúcich vzťahy dedičnosti.

Obr

Príklad hierarchie tried

Predpokladajme napríklad, že máme objekt triedy E prístupný prostredníctvom ukazovateľa pe. Tento ukazovateľ môžeme pretypovať na ukazovateľ na triedu C priamo (to je dovolené – pretypujeme potomka na predka):

C* pc = pe;

alebo pomocou nového operátora:

C* pc = dynamic_cast<C*>(pe);

Ak máme objekt triedy E prístupný pomocou ukazovateľa pa na triedu A (ktorý de facto ukazuje na zdedený podobjekt triedy A v objekte triedy E), môžeme sa k ukazovateľu na celý objekt E dostať takto:

E* pe = dynamic_cast<E*>(pa);

Tu už na rozdiel od predchádzajúceho príkladu priamy spôsob pretypovania nemôžeme použiť.

Operátor dynamic_cast však umožňuje aj pretypovanie ukazovateľa na podobjekt triedy B na ukazovateľ na podobjekt triedy D (stále za predpokladu, že v skutočnosti pracujeme s objektom triedy E):

B* pb = pe;
D* pd = dynamic_cast<D*>(pb);

Cieľovým typom operátora dynamic_cast môže byť aj typ void*. V takomto prípade je výsledkom pretypovania ukazovateľ na celý objekt, ktorého sa pretypovanie týka. Ak teda máme napríklad ukazovateľ pd z predchádzajúceho príkladu, po pretypovaní:

void* pv = dynamic_cast<void*>(pd);

bude v ukazovateli pv adresa celého objektu triedy E.

V prípade, že pretypovanie nie je možné, operátor vráti nulovú hodnotu (pri pretypovaní ukazovateľov) alebo vyhodí výnimku bad_cast (pri pretypovaní referencií).

Uveďme si ešte príklad, ktorý ukáže použitie operátora dynamic_cast v praxi:

class B
{
public:
  virtual void f() {}
};
 
class D : public B
{
public:
  virtual void f() {}
  void g() { printf("Hello\n"); }
};
 
void fnc(B* pb)
{
  D* pd = dynamic_cast<D*>(pb);
  if (pd)
    pd->g();
}
 
void main()
{
  D d;
  fnc(&d);
}

Funkcia fnc() testuje, či jej argument, ukazovateľ s doménovým typom B, náhodou neukazuje na objekt triedy D. Ak zistí, že áno, zavolá nad týmto objektom jeho členskú funkciu g(). Ak si vo funkcii main() zmeníte typ premennej d na triedu B, uvidíte, že k volaniu funkcie D::g() nedôjde.

Operátor static_cast

Na rozdiel od predchádzajúceho operátora nepoužíva operátor static_cast dynamickú identifikáciu typu. To ostatne naznačuje už jeho názov. Pri použití operátora static_cast sa prekladač riadi len statickým, v dobe prekladu známym typom pretypovávaného objektu. Syntax operátora je podobná ako pri dynamic_cast:

static_cast < cieľový typ > ( výraz )

Pokus o pretypovanie objektovej inštancie na triedu, ktorá nie je ani jej predkom, ani potomkom, prekladač bez problémov povolí, ale výsledok nie je definovaný a jeho použitie môže viesť až k zrúteniu programu. Ukážme si príklad (predpokladáme triedy B a D z predchádzajúceho odseku):

void f(B* pb)
{
  D* pd = static_cast<D*>(pb);
  pd->g();
}

Vo funkcii f() sa snažíme pretypovať ukazovateľ na triedu B na ukazovateľ na jej potomka, triedu D. Keďže je v programe zapísané (a známe už počas prekladu), že chceme pretypovať ukazovateľ pb, ktorý je typu B*, na ukazovateľ pd typu D*, prekladač sa nebude sťažovať a hlavne si nijako nebude overovať, či pb naozaj ukazuje na inštanciu triedy D (to napokon počas prekladu ani nejde). Teraz si predstavme, že funkcii f() odovzdáme ako skutočný argument ukazovateľ na inštanciu triedy B. Pretypovaním dostaneme ukazovateľ typu D*, v skutočnosti však ukazujúci na typ B. Volanie členskej funkcie D::g() nad objektom triedy B sa skončí pravdepodobne zle.

Operátor static_cast nie je obmedzený len na pretypovanie objektových typov. V skratke možno povedať, že tento operátor je ekvivalentom rozumného použitia pôvodného pretypovacieho operátora a mal by sa namiesto neho používať všade tam, kde chceme explicitne vyjadriť konverziu medzi dvoma typmi. Nasledujúce príklady ukazujú jeho použitie:

void* pv = "Hello";
char* pc = static_cast<char*>(pv);
float f = 123.456;
double d = static_cast<double>(f);
char c = 'Z';
int i = static_cast<int>(c);

Operátor const_cast

Všetci veľmi dobre poznáme význam a použitie špecifikátorov const a volatile. Takisto všetci vieme, že na to, aby sme obišli zábrany, ktoré z ich použitia vyplývajú, nám stačí použiť vhodne pretypované ukazovatele či referencie. Operátor const_cast je určený práve na takéto pretypovávanie. Ako jediný zo všetkých štyroch nových operátorov totiž dokáže pridávať či odstraňovať špecifikátory const a volatile z existujúcich výrazov. Syntax jeho použitia asi nikoho neprekvapí:

const_cast < cieľový typ > ( výraz )

Cieľový typ a typ pretypovávaného výrazu sa môžu líšiť maximálne v počte a umiestnení špecifikátorov const a volatile.

Použitie operátora const_cast v praxi je zrejmé. Jeden príklad za všetky:

const int ci = 10;
const int* pci = &ci;
*pci = 20;  // chyba
int* pi = const_cast<int*>(pci);
*pi = 20;   // OK

Operátor reinterpret_cast

Posledný zo štvorice operátorov má na starosti doslova „špinavú“ robotu. Je určený na pretypovanie ukazovateľov medzi sebou, na konverziu ukazovateľov na celé čísla a naopak a všeobecne na všetky tie implementačne závislé, neprenosné, nízkoúrovňové prevody, ktoré tak často a radi používame, pretože nám ušetria kopu starostí. Jeho použitie nie je príliš bezpečné, v podstate jediným zaručene bezpečným výsledkom je pretypovanie „tam a naspäť“, po ktorom dostaneme opäť pôvodnú hodnotu.

Syntax operátora reinterpret_cast nie je ničím výnimočná:

reinterpret_cast < cieľový typ > ( výraz )

Pre istotu je tu ešte malý príklad použitia:

char* p = new char[100];
int addr = reinterpret_cast<int>(p);

V premennej addr bude celočíselná reprezentácia adresy miesta v pamäti, kde je uložené alokované pole, 100 znakov.

Dynamická identifikácia typu

Pri opise operátora dynamic_cast sme si spomenuli, že na zisťovanie skutočného typu objektu sa používa dynamická identifikácia typu. V tejto súvislosti máme v C++ k dispozícii nový operátor typeid, ktorý vracia pre daný objekt referenciu na konštantnú inštanciu triedy type_info, opisujúcu typ tohto objektu. Syntax operátora typeid je dvojaká:

typeid ( výraz )
typeid ( typ )

Prvý spôsob slúži na zisťovanie typu určitého výrazu, druhý je v podstate ekvivalentom klasických literálov – pre daný explicitne vyjadrený typ vracia jeho identifikáciu.

Trieda type_info je deklarovaná v hlavičkovom súbore <typeinfo.h> a obsahuje okrem iného konštantnú členskú funkciu name(), vracajúcu názov daného typu (ako char*), a dva prekryté operátory == a != na porovnávanie inštancií tejto triedy. Pokiaľ je možné vyhodnotiť operátor typeid už počas prekladu, stane sa tak. Dynamicky, za behu programu, sa vyhodnocuje len typ polymorfných objektov, prezentovaných tomuto operátoru pomocou referencie alebo dereferencovaného ukazovateľa. V prípade, že by operátor typeid mal dereferencovať nulový ukazovateľ, vyhodí výnimku typu bad_typeid.

Operátory new a delete

Pri rozprávaní o dynamickej alokácii premenných sme si povedali, že operátor new pri neúspešnej alokácii vráti nulový ukazovateľ. Norma ANSI mu však stanovuje trochu odlišné správanie. Štandardná verzia operátora new (resp. operátorovej funkcie operator new()) pri nemožnosti alokovať požadované množstvo pamäte musí vyhodiť výnimku bad_alloc. V záujme zachovania možnosti používať pôvodný spôsob alokácie existuje aj verzia, ktorá vracia nulový ukazovateľ. Tú špecifikujeme pomocou dodatočného argumentu nothrow. Tento argument je v skutočnosti prázdna štruktúra, definovaná len na odlíšenie oboch verzií operátora new.

Vzhľadom na existenciu uvedených rozdielov sa trochu menia prototypy operátorových funkcií operator new(). Keď ich chceme prekryť vlastnými funkciami, mali by sme dodržať nasledujúcu sémantiku:

void* operator new(size_t) throw(bad_alloc);
void* operator new(size_t, const nothrow_t&) throw();

Typ nothrow_t je typom spomínanej štruktúry nothrow.

Druhá novinka sa týka alokácie polí. Operátor new pri alokácii poľa objektov volá funkciu operator new[](). Túto funkciu, ktorá zabezpečuje pridelenie dostatočne veľkého úseku pamäte, môžeme, samozrejme, prekryť vlastnou funkciou. Jej presný prototyp (bez pridaných voliteľných argumentov) je:

void* operator new[](size_t);

Povinný argument tejto funkcie typu size_t predstavuje celkovú veľkosť alokovaného poľa, návratová hodnota predstavuje ukazovateľ na alokované miesto v pamäti. (V skutočnosti existujú dva rôzne prototypy, líšiace sa zoznamom výnimiek, ktoré funkcia vyhadzuje – pozri predchádzajúci text).

Podobne operátor delete pri dealokácii poľa objektov volá funkciu operator delete[](). Jej dva základné prototypy (opäť bez pridaných voliteľných argumentov) sú:

void operator delete[](void*);
void operator delete[](void*, size_t);

Na zopakovanie: Prvý argument predstavuje ukazovateľ na dealokovanú oblasť, prípadný druhý určuje jej veľkosť.

Príkazy selekcie

Medzi príkazy selekcie čiže výberu (myslí sa výber medzi viacerými alternatívami budúceho toku programu) patria príkazy if, switch, for, while a do. Podľa normy ANSI je povolené v podmienkových výrazoch týchto príkazov (s výnimkou príkazu do) deklarovať nové premenné. Rozsah platnosti takto deklarovaných premenných sa obmedzuje na blok tvoriaci telo príslušného príkazu. Ukážme si príklad:

while (char c = getchar())
{
  // ...
}
c = 'A';  // chyba!

Pokus o prístup k premennej c mimo bloku while má za následok chybu pri preklade.

Rovnaké pravidlá platia pre ostatné príkazy selekcie. Za zmienku stojí hádam iba príkaz for, v ktorom bolo možné deklarovať premenné už predtým, ale iba v inicializačnom príkaze (prvá časť zátvorky pred bodkočiarkou). Podľa normy ANSI môžeme deklarovať nové premenné aj v testovacom výraze (v strede medzi oboma bodkočiarkami). Okrem toho niektoré prekladače nedodržujú presne pravidlo o obmedzení rozsahu platnosti premenných na telo príkazu for, takže je možné takto deklarované premenné používať aj v ďalšom kóde.

Členy tried

Vieme, že premenné objektových typov môžeme definovať so špecifikátorom const. Takéto premenné sa považujú za konštantné, ich zložky nemôžeme meniť a dokonca môžeme nad nimi vyvolať iba tie členské funkcie, ktoré sme deklarovali ako konštantné (opäť pomocou kľúčového slova const). Tieto členské funkcie nesmú za normálnych okolností nijako modifikovať stav objektu.

Môže sa nám však pri návrhu objektových typov stať, že budeme potrebovať zaviesť do definície triedy taký údajový člen, ktorý by mal byť modifikovateľný za všetkých okolností, teda aj pre konštantné inštancie danej triedy. Pre takúto situáciu máme v C++ k dispozícii nový špecifikátor mutable, použiteľný len pri deklarácii údajových členov tried, ktorý povoľuje modifikáciu týchto členov aj z konštantných členských funkcií. Tu je krátky príklad:

class C
{
  int a;
  mutable int b;
public:
  void f();
  void g() const;
};
 
void C::f()
{
  a = 1;  // OK
  b = 2;  // OK
}
 
void C::g() const
{
  a = 3;  // chyba!
  b = 4;  // OK
}
 
void main()
{
  C c1;
  const C c2;
  c1.f();  // OK
  c1.g();  // OK
  c2.f();  // chyba
  c2.g();  // OK
}

V konštantnej členskej funkcii g() nesmieme modifikovať zložku a, môžeme však ľubovoľne pracovať so zložkou b, deklarovanou v triede ako mutable. Ostatné pravidlá pre volanie členských funkcií nad konštantnými a nekonštantnými objektmi zostávajú v platnosti, t. j. nad premennou c2 môžeme zavolať len členskú funkciu g(), volanie c2.f() prekladač odmietne preložiť.

Explicitné konštruktory

Isto si spomeniete, že v časti venovanej konštruktorom sme sa zmienili o tzv. implicitných konverziách na objektové typy. Vždy, keď prekladač potrebuje konvertovať hodnotu jedného (objektového či neobjektového) typu na iný objektový typ, môže tak urobiť buď pomocou konverznej funkcie, čo nie je nič iné ako prekrytý operátor pretypovania, alebo pomocou konštruktora s práve jedným pevným argumentom vhodného typu. Na osvieženie pamäti príklad:

class C
{
public:
  C(int);
};
 
void f(C) {}
 
void main()
{
  f(1);
  C c = 2;
}

Trieda C obsahuje konštruktor s jedným argumentom typu int. V programe máme definovanú funkciu f() s jedným argumentom typu C, ktorú vo funkcii main() zavoláme s argumentom typu int. Prekladač pri konverzii hodnoty 1 typu int na typ C použije spomínaný konštruktor C::C(int). Druhý prípad implicitnej konverzie nastáva pri inicializácii premennej c typu C hodnotou 2. Vieme, že riadok

C c = 2;

je ekvivalentný riadku

C c = C(2);

takže pri inicializácii objektu c sa implicitným volaním konštruktora C::C(int) vytvorí dočasný objekt C(2). Tento objekt sa odovzdá ako argument kopírovaciemu konštruktoru, ktorý na základe neho vytvorí objekt c (v našom prípade sme nedefinovali kopírovací konštruktor, použije sa preto implicitný, vytvorený prekladačom).

Nie vždy nám však bude takéto správanie prekladača vyhovovať. V takom prípade máme možnosť pomocou kľúčového slova explicit zakázať akékoľvek implicitné konverzie pomocou daného konštruktora. Špecifikátor explicit môžeme použiť iba v rámci deklarácie triedy a len pre konštruktory. Pridať ho môžeme aj k iným ako jednoparametrovým konštruktorom, nemá to však zmysel, pretože tieto iné konštruktory sa nemôžu zúčastňovať implicitných konverzií.

Prepíšeme teda deklaráciu triedy C z predchádzajúceho príkladu takto:

class C
{
public:
  explicit C(int);
};

Teraz oba pokusy o implicitnú konverziu zlyhajú a prekladač ohlási chybu. Ak chceme, môžeme prekladaču explicitne povedať, že danú konverziu požadujeme:

f(C(1));
C c = C(2);

Zmysel explicitných konštruktorov je predovšetkým v zabránení konštruovania nežiaducich objektov prekladačom v rozpore s úmyslami programátora.

Výnimky

V kategórii výnimiek je súčasťou normy jedna nová vlastnosť, o ktorej však mám minimum informácií, a pokiaľ viem, dosiaľ neexistuje prekladač, ktorý by ju implementoval. Ide o to, že pri definícii konštruktora môžeme explicitne uviesť celé jeho telo priamo ako try blok. Rozdiel oproti bežnému spôsobu, keď je try blok vnorený v tele konštruktora, je v tom, že v prvom prípade dokážeme zachytiť aj výnimky, ktoré vzniknú pri inicializácii členov danej triedy na základe zoznamu inicializátorov, uvedeného za dvojbodkou a názvom konštruktora.

Priestory mien

Poslednou novinkou, o ktorej si dnes povieme, sú priestory mien. Predstavte si väčší tím vývojárov, ktorí pracujú na nejakom rozsiahlejšom programe. Bežná situácia, ktorá si však vyžaduje pomerne veľkú disciplínu pri dodržiavaní pomenovacích konvencií pre funkcie či premenné, aby nedošlo ku kolíziám medzi názvami z rôznych modulov od rôznych členov tímu. Priestory mien pomáhajú zabraňovať týmto kolíziám veľmi jednoduchým spôsobom: každé deklarované meno môže byť priradené do svojho príslušného priestoru mien. Z hľadiska prístupu a viditeľnosti obsiahnutých mien sa priestory mien podobajú triedam. Mená deklarované v rozdielnych priestoroch mien môžu byť rovnaké a napriek tomu nedôjde k nijakej kolízii.

Deklarácia priestoru mien je veľmi priamočiara:

namespace meno { deklarácie }

Kľúčové slovo namespace je nasledované nepovinným menom deklarovaného priestoru mien. V zložených zátvorkách sa nachádzajú bežné deklarácie globálnych premenných, funkcií, tried či šablón. Ak sa v jednom súbore nachádza niekoľko namespace deklarácií s rovnakým menom, prekladač ich spojí do jednej. Priestory mien môžu byť vnorené. Ak neuvedieme meno priestoru, vytvoríme tzv. anonymný priestor mien. Všetky deklarácie v takomto priestore sa považujú za statické, a teda neviditeľné mimo daného súboru.

Pre už deklarovaný priestor mien môžeme zaviesť ľubovoľný počet jeho aliasov (synoným) pomocou deklarácie:

namespace meno1 = meno2 ;

Od tejto deklarácie bude meno2 ekvivalentným názvom priestoru mien meno1.

Prístup k menám deklarovaným v rámci nejakého priestoru mien je podobný prístupu k členom triedy: pred sprístupňované meno musíme uviesť meno priestoru spolu s operátorom :: (štvorbodka). Funkcie deklarované vnútri priestoru mien môžeme definovať (t. j. uviesť ich telo) aj mimo deklarácie namespace, opäť však musíme explicitne pomocou štvorbodky k názvu funkcie doplniť meno priestoru, do ktorého patrí. Telo takýchto funkcií je takisto súčasťou príslušného priestoru mien.

Pokiaľ chceme kdekoľvek v programe používať mená deklarované v nejakom priestore mien bez nutnosti zakaždým uviesť ich plne kvalifikované meno, pomôže nám kľúčové slovo using. Použiť ho môžeme dvoma spôsobmi – rozlišujeme tzv. deklaráciu using a direktívu using.

Syntax deklarácie using je takáto:

using meno :: identifikátor ;

Uvedením tejto deklarácie si sprístupňujeme identifikátor deklarovaný v priestore mien s názvom meno priamo, bez nutnosti plne ho kvalifikovať.

Direktíva using naproti tomu takto sprístupňuje všetky identifikátory v danom priestore mien:

using namespace meno ;

Napríklad všetky deklarácie, ktoré sú súčasťou štandardnej knižnice C++, patria do jedného priestoru mien s názvom std. Ak teda chceme so štandardnou knižnicou pracovať bez nutnosti zakaždým uvádzať kvalifikačný reťazec std::, použijeme na začiatku programu direktívu:

using namespace std;

Aby sme však len neteoretizovali, nakoniec si ukážeme krátky príklad:

int i = 1;
namespace A
{
  int i = 2;
  void f(double);
}
 
void A::f(double d)
{ /* ... */ }
 
void main()
{
  int i = 3;
  printf("%n\n"; i);     // lokálne i
  printf("%n\n"; A::i);  // A::i
  printf("%n\n"; ::i);   // globálne i
}

Funkcia f(), ktorá je deklarovaná v rámci priestoru mien A, má svoje telo uvedené mimo deklarácie namespace, musíme preto pri jej definícii uviesť plne kvalifikované meno A::f(). Ďalej v príklade máme deklarovanú globálnu premennú i s hodnotou 1 mimo akéhokoľvek priestoru mien. Okrem nej máme ešte jednu globálnu premennú i s hodnotou 2, ktorá je však súčasťou priestoru mien A. Vo funkcii main() sa nachádza deklarácia lokálnej premennej i s hodnotou 3. Jednotlivé premenné i sú prístupné spôsobom zrejmým z príkladu. Všimnite si, že aj tu na prístup k zakrytému globálnemu menu môžeme použiť unárny operátor ::.

Posledné kolo

Pokiaľ sledujete seriál pravidelne, viete, že budúca časť bude posledná. Zostáva nám už len povedať si pár slov o štandardnej knižnici jazyka C++ a tým naše takmer dvojročné úsilie zavŕšime.


Nechajte si posielať prehľad najdôležitejších správ emailom

Mohlo by Vás zaujímať

Ako na to

Tipy a triky: Ako na snímku obrazovky na akomkoľvek počítači s Windows?

02.12.2016 00:13

Ak snímky obrazovky robíte často apotrebujete napríklad funkcie na posun stránok alebo snímanie zobrazenia pri vyššom rozlíšení displeja, zrejme používate nejakú špecializovanú aplikáciu. Väčšina použ ...

Ako na to 1

Tipy a triky: Ako aplikácii prednastaviť spúšťanie s administrátorskými právami?

30.11.2016 00:10

Väčšina aspoň trochu skúsenejších používateľov vie, že aj keď máte na operačnom systéme Windows vytvorený administrátorský účet, aplikácie pre bezpečnosť nefungujú vždy splnými administrátorskými práv ...

Ako na to 2

Tipy a triky: Ako vypnúť uzamykaciu obrazovku vo Windows 10?

29.11.2016 00:10

Rozčuľuje vás, že pred každým prihlásením doúčtu vášho počítača musíte prejsť uzamykacou obrazovkou? Windows 10 na tejto obrazovke ukazuje čas,dátum anejakú zaujímavú fotografiu zrôznych kútov sveta. ...

Žiadne komentáre

Vyhľadávanie

Kyocera - prve-zariadenia-formatu-a4-s-vykonom-a3

Najnovšie videá