Vstup a výstup, prostory jmen
Prostory jmen jsou rozšířením pojmenovacích schopností jazyka C. Zabraňují kolizím jmen mezi jednotlivými součástmi kódu. Standardní vstup a výstup lze spravovat pomocí funkcí z jazyka C nebo pomocí proudů, které byly zavedeny v jazyce C++.
Prostory jmen
V jazyce C jsme měli poměrně omezené možnosti jak rozlišit dva identifikátory. Jediný možný prvek byl jméno identifikátoru. V rozsáhlých aplikacích, kde se používá kód třetích stran, se ale snadno mohlo stát, že došlo ke kolizi ve jménech často se vyskytujících funkcích nebo konstantách. Jazyk C++ zavedl technologii jmenných prostorů, která řeší problém pojmenování. Na jmenný prostor se dá dívat jako na jméno balíku zdrojového kódu. Pokud například vytváříme knihovnu, o které tušíme, že ji bude používat i někdo jiný, můžeme vytvořit prostor jmen s názvem naší firmy a umístit do něj všechny funkce naší knihovny. Tím zajistíme, že když pak někdo použije naši knihovnu, nebudou jména v ní kolidovat s jinými jmény.
Prostor jmen vytvoříme následovně:
namespace <jméno>
{
// obsah prostoru jmen
}
Prostor jmen se vytváří pomocí klíčového slova namespace
. vše, co umístíme mezi složené závorky, bude součástí prostoru jmen se jménem jméno. Ke členům prostoru jmen se přistupuje pomocí nově zavedeného rozlišovacího operátoru čtyřtečka ::
. Operaci se říká kvalifikace jména.
Pokud deklaraci neumístíme do žádného prostoru jmen, bude vždy umístěna do globálního prostoru jmen, ke kterému se dostaneme tak, že před kvalifikační operátor nic nezapíšeme. V následujícím kódu je ukázáno, jak se dotážeme na člen prostoru jmen:
namespace Prostor
{
int a;
}
// deklarace v globálním prostoru jmen
int a;
int main (int argc, char **argv)
{
// přistupujeme do prostoru Prostor
Prostor::a = 1;
// přistupujeme do globálního prostoru jmen, čtyřtečka není nutná
::a = 2;
return 0;
}
Je jasné, že pracujeme-li s rozsáhlým prostorem jmen a využíváme hodně jeho členů, bude neustálé kvalifikování členů velmi otravné. K usnadnění tohoto úkolu slouží klíčové slovo using
.
#include <knihovna.h>
// globální kvalifikace, uvedení celého prostoru jmen
using namespace Knihovna;
// globální kvalifikace, uvedení člena
using Knihovna::y;
int main (int argc, char **argv)
{
// lokální kvalifikace
using Knihovna::x;
x = 1;
y = 2;
return 0;
}
Klíčové slovo using
má působnost jen v mezích bloku, kde bylo použito. Pokud ho uvedeme mimo tělo jakékoliv funkce, bude platit pro celý soubor. V kódu výše očekáváme, že v hlavičkovém souboru knihovna.h
máme uveden jmenný prostor Knihovna
. Pomocí using namespace
se zbavíme nutnosti kvalifikovat každý ze členů prostoru jmen. Na druhou stranu se opět vystavujeme riziku kolizí, neboť po použití using namespace
prostor jmen začne postrádat smysl. Proto je mnohdy lepší použít pouze částečnou kvalifikaci, v níž vyjmenujeme jen členy, se kterými pracujeme. Tento postup je naznačen na řádce šest a jedenáct.
Jazyk C++ využívá výhod prostorů jmen a deklaruje všechny funkce standardní šablonové knihovny v prostoru jmen std
.
Vstup a výstup v C++
Již v první programu jsme se setkali s výpisem na standardní výstup. V jazyce C++ se pro komunikaci se soubory (tedy i se vstupem a výstupem) komunikuje pomocí proudů. Starší systém založený na funkcích printf()
a scanf()
je též možné použít, stačí pouze vložit hlavičku cstdio
. Instance proudů pro standardní vstup a výstup jsou automaticky vytvořeny při startu programu. Stačí pouze vložit hlavičku iostream
v níž nalezneme instanci cout
pro výpis hodnot na standardní výstup, cin
pro načítání hodnot ze standardního vstupu a cerr
pro výpis hodnot na standardní chybový výstup. Systém práce s proudy je značně nepodobný technikám z jazyka C. Základem pro práci je operátor <<
a >>
, který slouží ke směrování hodnot na výstup, popř. ke směrování vstupu do proměnné. Použití těchto operátorů lze řetězit a vytvářet tak proud dat směřující na výstup nebo ze vstupu. Odtud jméno proudy.
int a, b, c;
std::cin >> a >> b >> c;
std::cout << "promenna a: " << a << "\npromenna b: " << b << std::endl;
std::cerr << "promenna c na chybovem vystupu: " << c << std::endl;
Ve výše uvedeném kódu načteme tři proměnné a
, b
a c
. Všimněme si zřetězení i směru šipek. Šipky symbolicky ukazují směr proudu dat, tedy ze vstupu do proměnných, popř. z proměnných na výstup. Díky použití referencí není nutné předávat adresu proměnných, jako to bylo nutné v případě použití funkce scanf()
. Na další řádce vypíšeme dvě proměnné. Naši pozornost by mělo upoutat bezproblémové použití různých typů, které jsou vypisovány stejným způsobem. Ve funkci printf()
jsme museli specifikovat typ dat pomocí masky, při použití proudů toto není nutné, protože technika přetěžování operátorů nám umožňuje specifikovat různé chování podle zadaných dat a překladač pak vybere vhodný operátor pro ten který typ. Jde o velmi mocnou techniku, o které se zmíníme později.
Manipulátory proudů
Ve výpisu výše se vyskytovalo slovo endl
. Jde o jeden ze manipulátorů, stavových přepínačů, které ovlivňují stav proudu. Až na endl
jsou všechny manipulátory deklarované v hlavičkovém souboru iomanip
. Manipulátory se vkládají do proudu a ovlivňují výslednou podobu výpisu či načítání. Některé manipulátory mají globální efekt, jiné se aplikují jen na nejbližší následující operaci. Seznam několika z nich nyní uvedeme:
- boolalpha/noboolalpha
- Zapne nebo vypne vypisování logických hodnot slovy
true
afalse
. Ve výchozím stavu je vypnuto. - dec, hex, oct
- Nastaví bázi pro práci s čísly na po řadě desítkovou, šestnáctkovou a osmičkovou.
- endl
- Ukončí proud a vloží znak nového řádku. Veškerá vyrovnávací paměť je vyprázdněna. Časté použití tohoto manipulátoru vede k degradaci výkonnosti. Pokud požadujeme pouze odřádkování, je lepší vložit do proudu znak nového řádku
\n
ručně, jak je ukázáno ve výpisu výše. - ends
- Přidá do proudu nulový znak
\0
. - fixed, scientific
- Nastaví formát vypisování čísel na v pořadí fixní umístění desetinné tečky nehledě na exponent a vědecký výpis. Neuvedeme-li žádný formát, bude použit interní formát.
- flush
- Vyprázdní vyrovnávací paměť proudu. Je implicitně prováděno při použití
endl
. - internal, left, right
- Nastaví zarovnávání ve sloupci v pořadí interní systém zarovnávání, zarovnání doleva a zarovnání doprava.
- showbase/noshowbase
- Zobrazí nebo nezobrazí bázi (0x u šestnáctkové a 0 u osmičkové). Ve výchozím nastavení je vypnuto.
- showpoint/noshowpoint
- Vynutí nebo nevynutí vypsání desetinné tečky u dekadických hodnot. Ve výchozím nastavení je vypnuté.
- showpos/noshowpos
- Vynutí psaní znaménka i u kladných hodnot. Ve výchozím nastavení je vypnuto.
- skipws/noskipws
- Ignoruje více bílých znaků za sebou a chápe je jako jediný. Ve výchozím nastavení je zapnuto.
- uppercase/nouppercase
- Vynutí psaní velkých nebo malých písmen u proudem generovaných textů jako je například šestnáctková báze.
- setbase(int)
- Přejímá hodnoty 8, 10 a 16. Ekvivalent
dec
,hex
aoct
. - setfill(char)
- Nastaví vyplňovací znak. Ve výchozím nastavení je to mezera.
- setprecision(int)
- Nastaví přesnost vypisování necelých čísel. Interpretace závisí na na formě vypisování čísel. Je-li použit výchozí formát vypisování čísel, je údaj přesnosti interpretován jako maximum platných číslic před i za tečkou. Je-li použit formát vypisování
fixed
neboscientific
, je uvedené číslo rovno počtu číslic za desetinnou tečkou. - setw(int)
- Nastaví šířku sloupce pro následující vypisovanou proměnnou. Tento manipulátor je nutné uvést pro každý výpis zvlášť.
Pokud tedy chceme vypsat proměnnou a v šestnáctkové soustavě, zarovnanou do sloupce šířky deset a s přesností na osm číslic, provedeme to následovně.
#include <iostream>
#include <iomanip>
// ...
std::cout << std::hex << std::setw(10) <<
std::setprecision(8) << a << std::endl;