-
Organizace ISO a ANSI se sna₧φ standardizovat programovacφ jazyk C++. Jednφm
v²sledkem tΘto standardizace je Knihovna standardnφch Üablon (STL
- pochßzφ od firmy Rogue Wave's). Jednß se o rozsßhlou knihovnu datov²ch
struktur a algoritm∙.
Hlavnφ Φßstφ STL je kolekce definicφ t°φd pro standardnφ datovΘ struktury
a kolekce algoritm∙ pou₧φvan²ch k manipulaci s t∞mito strukturami. Struktura
STL se liÜφ v mnoha ohledech od ostatnφch knihoven C++.
Zßkladem pro pou₧itφ t°φd kontejner∙ a p°i°azen²ch algoritm∙ poskytnut²ch
standardnφ knihovnou je koncepce iterßtor∙. Abstraktn∞, iterßtor je jednoduch²
objekt, podobajφcφ se ukazateli, pou₧it² k prochßzenφ p°es vÜechny prvky
ulo₧enΘ v kontejneru. Proto₧e v r∙zn²ch typech kontejner∙ se prochßzφ p°es
prvky r∙zn²m zp∙sobem, jsou r∙znΘ typy iterßtor∙. Ka₧dß t°φda kontejneru
ve standardnφ knihovn∞ m∙₧e generovat iterßtor s pot°ebnou funkΦnostφ pro
uklßdacφ techniku pou₧itou p°i implementaci kontejneru.
Stejn∞ jako ukazatelΘ mohou b²t v tradiΦnφm programovßnφ pou₧ity k
r∙zn²m ·Φel∙m, takΘ iterßtory jsou pou₧φvßny pro mnoho r∙zn²ch Φinnostφ.
Iterßtor m∙₧e b²t pou₧it k odkazovßnφ na specifickou hodnotu, stejn∞ jako
ukazatel k odkazovßnφ na jistΘ mφsto pam∞ti. Na druhΘ stran∞ dvojice iterßtor∙
m∙₧e b²t pou₧ita k popisu rozsahu hodnot, stejn∞ jako dva ukazatelΘ mohou
b²t pou₧ity k urΦenφ souvislΘ oblasti pam∞ti. V p°φpad∞ iterßtor∙ se ale
nemusφ nutn∞ jednat o fyzickou sekvenci prvk∙, ale o logickou sekvenci,
nebo¥ je zalo₧ena na typu kontejneru a prvky kontejneru jsou v po°adφ v
jakΘm je kontejner udr₧uje.
Normßlnφ ukazatelΘ mohou mφt n∞kdy hodnotu null, co₧ znamenß,
₧e na nic neukazujφ. Iterßtory takΘ nemusφ urΦovat specifickou hodnotu.
Jako je chyba dereferencovat ukazatel s hodnotou null je takΘ chybou
dereferencovat iterßtor, kter² neurΦuje hodnotu.
Kdy₧ dva ukazatelΘ popisujφ oblast v pam∞ti jsou pou₧ity v programu
C++, pak koncov² ukazatel m∙₧e ukazovat na mφsto, kterΘ ji₧ nemusφ pat°it
do oblasti. Nap°. pole nazvanΘ
x o dΘlce 10 prvk∙ m∙₧e b²t n∞kdy
popisovßno jako rozÜφ°enφ od x do
x+10, i kdy₧ prvek x+10
ji₧ nenφ souΦßstφ pole (ukazuje na hodnotu ulo₧enou za polem). K popisu
rozsahu se podobn∞ pou₧φvajφ i iterßtory. Druh² iterßtor takΘ urΦuje nßsledujφcφ
hodnotu v sekvenci za rozsahem. Nenφ vhodnΘ ale dereferencovat iterßtor,
kter² je pou₧it ke specifikaci konce rozsahu (m∙₧e to b²t indikace koncovΘ
hodnoty).
Stejn∞ jako u normßlnφch ukazatel∙, zßkladnφ operace pou₧φvanß k modifikaci
iterßtoru je operace inkrementace (operßtor ++). Kdy₧ operßtor inkrementace
je aplikovßn na iterßtor, kter² ukazuje na poslednφ hodnotu v sekvenci,
pak je zm∞n∞n na indikaci koncovΘ hodnoty.
Ve standardnφ knihovn∞ je pou₧φvßno p∞t zßkladnφch tvar∙ iterßtor∙:
-
vstupnφ iterßtor - pouze Φtenφ, pohyb dop°edu
-
v²stupnφ iterßtor - pouze zßpis, pohyb dop°edu
-
dop°edn² iterßtor - Φtenφ i zßpis, pohyb dop°edu
-
obousm∞rn² iterßtor - Φtenφ i zßpis, pohyb dop°edu i dozadu
-
iterßtor nßhodnΘho p°φstupu - Φtenφ i zßpis, nßhodn² p°φstup
Kategorie iterßtor∙ jsou hierarchickΘ. Dop°edn² iterßtor m∙₧e b²t pou₧it,
kdy₧ jsou po₧adovßny vstupnφ nebo v²stupnφ iterßtory, obousm∞rn² iterßtor
m∙₧e b²t pou₧it namφsto dop°ednΘho iterßtoru a iterßtor nßhodnΘho p°φstupu
m∙₧e b²t pou₧it v situacφch vy₧adujφcφch obousm∞rnost.
Druhou charakteristikou iterßtoru je to, zda m∙₧e b²t pou₧it k modifikaci
hodnot ulo₧en²ch v p°i°azenΘm kontejneru. Konstantnφ iterßtor je
ten, kter² m∙₧e b²t pou₧it pouze pro p°φstup, ale ne pro modifikaci. V²stupnφ
iterßtory nejsou nikdy konstantnφ a vstupnφ iterßtory jsou konstantnφ v₧dy.
Ostatnφ iterßtory mohou, ale nemusφ b²t konstantnφ, zßvisφ to na tom, jak
jsou vytvo°eny. Je tedy konstantnφ a nekonstantnφ obousm∞rn² iterßtor,
konstantnφ a nekonstantnφ iterßtor nßhodnΘho p°φstupu atd.
-
Vstupnφ iterßtory jsou nejjednoduÜÜφm tvarem iterßtoru. K pochopenφ
jejich mo₧nostφ p°edpoklßdejme p°φklad programu. Obecn² algoritmus find
(bude podrobn∞ popsßn pozd∞ji) provßdφ jednoduchΘ lineßrnφ hledßnφ specifikovanΘ
hodnoty v kontejneru. Obsah kontejneru je popsßn pomocφ dvou iterßtor∙,
kterΘ se jmenujφ first a last. Dokud first nenφ roven
last,
pak prvek urΦen² first je porovnßvßn s testovanou hodnotou. P°i
rovnosti je vrßcen iterßtor, kter² nynφ ukazuje na nalezen² prvek. P°i
nerovnosti, iterßtor first je inkrementovßn a cyklus pokraΦuje.
Pokud je prohledßna celß oblast pam∞ti bez nalezenφ po₧adovanΘ hodnoty,
pak algoritmus vracφ koncovou hodnotu.
template <class InputIterator, class T>
InputIterator
find (InputIterator first, InputIterator
last, const T& value)
{
while (first != last &&
*first != value)
++first;
return first;
}
Tento algoritmus ukazuje t°i po₧adavky na vstupnφ iterßror:
-
Iterßtor m∙₧e b²t porovnßvßn na rovnost s jin²m iterßtorem. Jsou si rovny,
pokud ukazujφ na stejnou pozici.
-
Iterßtor m∙₧e b²t dereferencovßn pomocφ operßtoru * k zφskßnφ hodnoty urΦenΘ
iterßtorem.
-
Iterßtor m∙₧e b²t pomocφ operßtoru ++ inkrementovßn, tak aby ukazoval na
nßsledujφcφ prvek v sekvenci.
Vstupnφ iterßtory majφ t°i hlavnφ varianty:
-
Normßlnφ ukazatele - mohou b²t pou₧ity jako vstupnφ iterßtory (p°φpadn∞
v²stupnφ iterßtory). Nap°. nßsleduje hledßnφ hodnoty 7 v poli cel²ch Φφsel:
int data[100];
...
int * where = find(data, data+100, 7);
-
Iterßtory kontejner∙ - vÜechny iterßtory vytvo°enΘ pro r∙znΘ kontejnery
poskytnutΘ standardnφ knihovnou jsou alespo≥ vstupnφmi iterßtory. Iterßtor
pro prvnφ prvek v kolekci je v₧dy vytvo°en metodou begin a iterßtor,
kter² indikuje koncovou hodnotu kontejneru je generovßn metodou end.
Nap°. nßsleduje hledßnφ hodnoty 7 v seznamu cel²ch Φφsel:
list<int>::iterator where = find(aList.begin(),
aList.end(), 7);
Ka₧d² kontejner, kter² podporuje iterßtory poskytuje v deklaraci t°φdy
typ se jmΘnem iterßtoru. Pokud kontejner bude zp°φstup≥ovßn jako konstantnφ
nebo pokud je pou₧it popis const_iterator, pak iterßtor je konstantnφm
iterßtorem.
-
Iterßtory vstupnφho proudu - standardnφ knihovna poskytuje mechanismus
k operovßnφ na vstupnφm proudu pomocφ vstupnφho iterßtoru. Tato schopnost
je poskytnuta t°φdou istream_iterator.
-
V²stupnφ iterßtor mß opaΦnou funkci ne₧ vstupnφ iterßtor. V²stupnφ
iterßtory mohou b²t pou₧φvßny k p°i°azovßnφ hodnot do sekvencφ, ale nemohou
b²t pou₧φvßny k zp°φstupn∞nφ hodnot. Nap°. v²stupnφ iterßtor m∙₧eme pou₧φt
v obecnΘm algoritmu, kter² kopφruje hodnoty z jednΘ sekvence do jinΘ:
template <class InputIterator, class OutputIterator>
OutputIterator copy
(InputIterator first, InputIterator
last, OutputIterator result)
{
while (first != last)
*result++
= *first++;
return result;
}
Rozsah zdrojov²ch hodnot je specifikovßn pßrem vstupnφch iterßtor∙
a cφl v²stupnφm iterßtorem (p°edpoklßdßme, ₧e obsahuje stejnΘ mno₧stvφ
hodnot, jako rozsah zdrojov²ch hodnot). Jak ukazuje tento algoritmus, v²stupnφ
iterßtor m∙₧e modifikovat prvek na kter² ukazuje a m∙₧e b²t tedy pou₧it
jako cφl pro p°i°azenφ. V²stupnφ iterßtory mohou b²t dereferencovßny pouze
tφmto zp∙sobem a nemohou b²t pou₧ity pro p°φstup k prvk∙m, na kterΘ ukazujφ.
Jak je uvedeno v²Üe, normßlnφ ukazatele, stejn∞ jako vÜechny iterßtory
vytvo°enΘ kontejnery ve standardnφ knihovn∞, mohou b²t pou₧ity jako v²stupnφ
iterßtory. Nßsledujφcφ Φßst k≤du kopφruje prvky z pole typu C do standardnφho
typu vector:
int data[100];
vector<int> newdata(100);
...
copy (data, data+100, newdata.begin());
Stejn∞ jako istream_iterator nabφzφ mo₧nost operovßnφ na vstupnφm
proudu pomocφ mechanismu vstupnφho iterßtoru, ostream_iterator umo₧≥uje
zapisovat hodnoty do v²stupnφho proudu.
Jin²m tvarem v²stupnφho operßtoru je vklßdacφ iterßtor. Vklßdacφ iterßtor
zm∞nφ operace v²stupu na vklßdßnφ do kontejneru. To umo₧≥uje pou₧φvßnφ
operace typu kopφrovßnφ s kontejnery prom∞nnΘ dΘlky, jako jsou seznamy
a mno₧iny.
-
Dop°edn² iterßtor kombinuje slu₧by vstupnφho iterßtoru a v²stupnφho
iterßtoru. Umo₧≥uje p°istupovat k hodnotßm i modifikovat hodnoty. Jednou
z funkcφ kterou pou₧φvß dop°edn² iterßtor je algoritmus replace,
kter² nahrazuje v²skyty specifikovan²ch hodnot jin²mi hodnotami. Tento
algoritmus je zapsßn takto:
template <class ForwardIterator, class
T>
void
replace (ForwardIterator first,
ForwardIterator last,
const T& old_value, const T& new_value)
{
while (first != last)
{
if (*first == old_value)
*first = new_value;
++first;
}
}
B∞₧n² ukazatel, stejn∞ jako libovoln² z iterßtor∙ vytvß°en² kontejnery
ve standardnφ knihovn∞, m∙₧e b²t pou₧it jako dop°edn² iterßtor. Nap°. nßsleduje
nahrazenφ instancφ hodnot 7 hodnotami 11 ve vektoru cel²ch Φφsel:
replace (aVec.begin(), aVec.end(), 7, 11);
-
Obousm∞rnΘ iterßtory se podobajφ dop°edn²m iterßtor∙m, s tou odchylkou,
₧e obousm∞rnΘ iterßtory podporujφ operßtor dekrementace (operßtor --).
To umo₧≥uje pohyby dop°edu a zp∞t po prvcφch kontejneru. Obousm∞rn² iterßtor
m∙₧eme pou₧φt nap°. ve funkci, kterß obracφ hodnoty kontejneru a umis¥uje
v²sledek do novΘho kontejneru.
template <class BidirectionalIterator,
class OutputIterator>
OutputIterator
reverse_copy (BidirectionalIterator
first, BidirectionalIterator last,
OutputIterator result)
{
while (first != last)
*result++ = *--last;
return result;
}
Jako v₧dy, hodnota p∙vodn∞ urΦenß parametrem last ji₧ nepat°φ
do kontejneru.
Funkce reverse_copy m∙₧e b²t nap°. pou₧ita k obracenφ hodnot
z°et∞zenΘho seznamu a umφst∞nφ v²sledku do vektoru:
list<int> aList;
....
vector<int> aVec (aList.size());
reverse_copy (aList.begin(), aList.end(),
aVec.begin() );
-
N∞kterΘ algoritmy vy₧adujφ v∞tÜφ schopnosti ne₧ mo₧nost p°istupovat k hodnotßm
ve sm∞ru dop°edu nebo dozadu. Iterßtory nßhodnΘho p°φstupu poskytujφ
hodnoty pro p°φstup nßhodn∞, podobn∞ jako normßlnφ ukazatelΘ. Kdy₧ pou₧φvßme
normßlnφ ukazatele, pak aritmetickΘ operace mohou b²t pou₧ity k urΦenφ
adresy pam∞ti; tj.
x+10 je pam∞¥ 10 prvk∙ od zaΦßtku x. S
iterßtory logick² v²znam je stejn² (x+10 je desßt² prvek za x),
ale fyzickß adresa se m∙₧e liÜit.
Algoritmy, kterΘ pou₧φvajφ nßhodnΘ p°φstupovΘ iterßtory zahrnujφ obecnΘ
operace jako je °azenφ a binßrnφ vyhledßvßnφ. Nap°. nßsledujφcφ algoritmus
nßhodn∞ zamφchß prvky kontejneru. Podobß se (ale je jednoduÜÜφ) funkci
random_shuffle
ze standardnφ knihovny:
template <class RandomAccessIterator>
void
mixup (RandomAccessIterator
first, RandomAccessIterator last)
{
while (first < last)
{
iter_swap(first,
first + randomInteger(last - first));
++first;
}
}
Program prochßzφ od pozice urΦenΘ first, kterß je d°φve v sekvenci
ne₧ pozice last. Pouze iterßtory nßhodnΘho p°φstupu mohou b²t porovnßvßny
pomocφ relaΦnφch operßtor∙; vÜechny ostatnφ iterßtory mohou b²t porovnßvßny
pouze na rovnost nebo nerovnost. P°i ka₧dΘm pr∙chodu cyklem, v²raz last
- first urΦuje poΦet prvk∙ mezi dv∞mi mezemi. Funkce randomInteger
je urΦena pro generovßnφ nßhodn²ch Φφsel mezi 0 a hodnotou parametru. Pomocφ
standardnφho generßtoru nßhodn²ch Φφsel tuto funkci m∙₧eme zadat takto:
unsigned int randomInteger (unsigned int
n)
{
return rand() % n;
}
Tato nßhodnß hodnota je p°iΦtena k iterßtoru first, co₧ zp∙sobφ,
₧e iterßtor nßhodn∞ vybφrß hodnotu v kontejneru. Tato hodnota je potom
vym∞n∞na s prvkem na kter² ukazuje iterßtor first.
-
Iterßtory p°irozen∞ dodr₧ujφ po°adφ hodnot p°ipojenΘho kontejneru. Pro
typy
vector nebo map po°adφ je dßno zv∞tÜujφcφmi se hodnotami
indexu. Pro set je to zv∞tÜujφcφ se hodnota prvk∙ dr₧en²ch v kontejneru.
Pro list po°adφ je explicitn∞ odvozeno od zp∙sobu vklßdßnφ hodnot.
Reverznφ iterßtory poskytujφ hodnoty p°esn∞ v opaΦnΘm po°adφ
ne₧ standardnφ iterßtory. Tj. pro vector nebo list reverznφ
iterßtor bude generovat poslednφ prvek prvnφ a prvnφ prvek poslednφ. Pro
set
je nejv∞tÜφ prvek generovßn prvnφ a nejmenÜφ poslednφ. Reverznφ iterßtory
tedy netvo°φ novou kategorii iterßtor∙. Jsou reverznφ obousm∞rnΘ iterßtory,
reverznφ iterßtory nßhodnΘho p°φstupu, atd.
DatovΘ typy list, set a map poskytujφ dvojice
metod, kterΘ vytvß°ejφ reverznφ obousm∞rnΘ iterßtory. Funkce rbegin
a rend generujφ iterßtory, kterΘ prochßzejφ p°ipojen²m kontejnerem
v opaΦnΘm po°adφ. Inkrementace p°esouvß iterßtor zp∞t a dekrementace dop°edu.
Podobn∞ datovΘ typy vector a deque poskytujφ funkce (takΘ
se jmenujφ rbegin a rend), kterΘ vytvß°ejφ reverznφ iterßtory
nßhodnΘho p°φstupu. Operßtor sΦφtßnφ stejn∞ jako inkrementace p°esouvß
iterßtor zp∞t v sekvenci.
-
ProudovΘ iterßtory jsou pou₧φvßny pro p°φstup k existujφcφmu vstupnφmu
nebo v²stupnφmu datovΘmu proudu pomocφ operacφ iterßtoru.
Jak ji₧ bylo uvedeno u popisu vstupnφch iterßtor∙, standardnφ knihovna
poskytuje mechanismus k zapojenφ vstupnφho proudu do vstupnφho iterßtoru.
Tato schopnost je poskytnuta t°φdou istream_iterator. P°i
deklaraci, Φty°i parametry Üablony jsou typ prvku, typ znakovΘho proudu,
typ rysu znaku a typ vzdßlenosti mezi prvky. Poslednφ dva parametry majφ
implicitnφ hodnoty: char_traits<charT > a ptrdiff_t. To
obvykle zajistφ vyhovujφcφ chovßnφ. Jeden parametr poskytnut² konstruktoru
pro istream_iterator je zp°φstup≥ovan² proud. P°i ka₧dΘm pou₧itφ
operßtoru ++ na iterßtor vstupnφho proudu je p°eΦtena a ulo₧ena novß hodnota
z proudu (pomocφ operßtoru >>). Tato hodnota je pak dostupnß pomocφ operßtoru
dereference (operßtor *). Hodnota vytvo°enß istream_iterator, kdy₧
nenφ poskytnut konstruktoru ₧ßdn² parametr, m∙₧e b²t pou₧ita jako koncovß
hodnota iterßtoru. Nap°. nßsledujφcφ k≤d hledß prvnφ hodnotu 7 v souboru
cel²ch Φφsel.
istream_iterator<int, char> intstream(cin),
eof;
istream_iterator<int, char>::iterator
where = find(intstream, eof, 7);
Prvek urΦen² iterßtorem pro vstupnφ proud je p°φstupn² pouze, dokud
nenφ po₧adovßn nßsledujφcφ prvek v proudu. Proto₧e iterßtor vstupnφho proudu
je vstupnφ iterßtor, prvky mohou b²t pouze zp°φstup≥ovßny a nelze je modifikovat
p°i°azenφm. Prvky mohou b²t zp°φstupn∞ny pouze jednou a to pouze sm∞rem
dop°edu. Pokud pot°ebujeme Φφst obsah proudu n∞kolikrßt, pak pro ka₧d²
pr∙chod musφme vytvo°it samostatn² iterßtor.
Mechanismus iterßtoru v²stupnφho proudu je analogick² iterßtoru vstupnφho
proudu. P°i ka₧dΘm p°i°azenφ hodnoty iterßtoru, je takΘ hodnota zapsßna
do p°i°azenΘho v²stupnΘho proudu pomocφ operßtoru >>. K vytvo°enφ
iterßtoru v²stupnφho proudu musφme specifikovat jako parametr v konstruktoru
p°i°azovan² v²stupnφ proud. Hodnoty zapisovanΘ do v²stupnφho proudu musφ
vyhovovat operaci >> proudu. Voliteln² druh² parametr konstruktoru je °et∞zec,
kter² bude pou₧it jako odd∞lovaΦ mezi ka₧dou dvojicφ hodnot. Nap°. nßsledujφcφ
k≤d kopφruje vÜechny hodnoty z vektoru na standardnφ v²stup a odd∞luje
hodnoty mezerami:
copy(newdata.begin(),newdata.end(),ostream_iterator<int,char>(cout,"
"));
Algoritmus jednoduchΘ transformace souboru m∙₧e b²t vytvo°en kombinacφ
iterßtor∙ vstupnφho a v²stupnφho proudu a r∙zn²ch algoritm∙ poskytnut²ch
standardnφ knihovnou. Nßsledujφcφ krßtk² program Φte soubor cel²ch Φφsel
ze standardnφho vstupu, odstra≥uje vÜechny v²skyty hodnot 7 a zbytek kopφruje
na standardnφ v²stup (ka₧dß hodnota je odd∞lena od°ßdkovßnφm):
void main()
{
istream_iterator<int, char>
input (cin), eof;
ostream_iterator<int, char>
output (cout, "\n");
remove_copy (input, eof, output,
7);
}
-
P°i°azovßnφ dereferencovanΘ hodnot∞ v²stupnφho iterßtoru je normßln∞ pou₧ito
pro p°epsßnφ obsahu existujφcφho mφsta. Nap°. nßsledujφcφ vyvolßnφ funkce
copy
p°enese hodnoty z jednoho vektoru do jinΘho, i kdy₧ mφsto pro druh² vektor
je ji₧ vytvo°eno (a p°φpadn∞ inicializovßno):
vector<int> a(10);
vector<int> b(10);
...
copy (a.begin(), a.end(), b.begin());
Tφmto zp∙sobem mohou b²t p°episovßny i struktury jako jsou seznamy.
Nßsledujφcφ p°φklad p°edpoklßdß, ₧e seznam nazvan² c mß alespo≥
10 prvk∙. PoΦßteΦnφch 10 mφst v seznamu c bude nahrazeno obsahem
vektoru a:
list<int> c;
...
copy (a.begin(), a.end(), c.begin());
U struktur jako jsou seznamy a mno₧iny, kterΘ se dynamicky p°i p°idßnφ
novΘho prvku zv∞tÜujφ, je ΦastΘ vlo₧enφ novΘ hodnoty do struktury mφsto
p°epsßnφ existujφcφho prvku. P°i pou₧itφ vklßdacφho iterßtoru jsou
prvky do p°i°azenΘho kontejneru vklßdßny (mφsto p°episovßnφ prvk∙ v kontejneru).
V²stupnφ operace iterßtoru je zm∞n∞na na vklßdßnφ do p°i°azenΘho kontejneru.
Nßsledujφcφ p°φklad nap°. vklßdß hodnoty vektoru do p∙vodn∞ prßzdnΘho seznamu:
list<int> d;
copy (a.begin(), a.end(), front_inserter(d));
Jsou t°i tvary vklßdacφch iterßtor∙ (vÜechny mohou b²t pou₧ity ke zm∞n∞
kopφrovacφ operace na vklßdacφ operaci). Iterßtor generovan² pomocφ front_inserter
(pou₧it² v²Üe) vklßdß prvek do Φela kontejneru. Iterßtor generovan² back_inserter
umφs¥uje prvek na konec do kontejneru. Oba tvary mohou b²t pou₧ity pro
typy list a deque, ale ne s mno₧inami a mapovßnφm. back_inserter,
ale ne front_inserter, m∙₧e b²t pou₧it i s vektorem. T°etφ nejobecn∞jÜφ
tvar je inserter, p°ebφrß dva parametry (kontejner a iterßtor v
kontejneru). Tento tvar kopφruje prvky na specifikovanΘ mφsto v kontejneru
(pro seznam to znamenß, ₧e prvky jsou kopφrovßny bezprost°edn∞ p°ed specifikovanou
pozici). Tento tvar m∙₧e b²t pou₧it se vÜemi strukturami, pro kterΘ pracujφ
p°edchozφ dva tvary a takΘ s mno₧inami a mapovßnφm.
Nßsledujφcφ jednoduch² program ukazuje pou₧itφ vÜech t°φ tvar∙ vklßdacφch
operßtor∙. Nejprve hodnoty 3, 2, a 1 jsou vlo₧eny do Φela prßzdnΘho seznamu.
PovÜimn∞te si jak jsou vklßdßny; ka₧dß je vlo₧ena na nov∞ vytvo°en² zaΦßtek,
a tak zφskßme v²slednΘ po°adφ 1, 2, 3. Dßle hodnoty 7, 8 a 9 jsou vlo₧eny
na konec seznamu. Nakonec operaci find pou₧ijeme k umφst∞nφ iterßtoru
na hodnotu 7 a bezprost°edn∞ p°ed nφ vlo₧φme Φφsla 4, 5 a 6. V²sledkem
je seznam Φφsel v po°adφ od 1 do 9.
void main() {
int threeToOne [ ] = {3, 2,
1};
int fourToSix [ ] = {4, 5, 6};
int sevenToNine [ ] = {7, 8,
9};
list<int> aList;
copy (threeToOne, threeToOne+3,
front_inserter(aList));
copy (sevenToNine, sevenToNine+3,
back_inserter(aList));
list<int>::iterator seven
= find(aList.begin(), aList.end(), 7);
copy (fourToSix, fourToSix+3,
inserter(aList, seven));
copy (aList.begin(),aList.end(),ostream_iterator<int,char>(cout,"
"));
cout << endl;
}
PovÜimn∞te si d∙le₧itΘho rozdφlu mezi iterßtory vytvo°en²mi inserter(aList,
aList.begin()) a front_inserter(aList). Volßnφ prvnφho kopφruje
hodnoty v po°adφ, p°idßvß ka₧d² z nich na zaΦßtek seznamu, zatφmco p°i
pou₧itφ druhΘho je ka₧dß hodnota p°ekopφrovßna na nov² zaΦßtek. V²sledkem
je to, ₧e front_inserter(aList) obracφ po°adφ p∙vodnφ sekvence,
zatφmco inserter(aList, aList.begin()) zachovßvß p∙vodnφ po°adφ.
-
Standardnφ knihovna poskytuje dv∞ funkce, kterΘ mohou b²t pou₧ity pro manipulaci
s iterßtory. Funkce advance p°ebφrß jako parametry iterßtor a Φφselnou
hodnotu a modifikuje iterßtor p°esunem o zadanou vzdßlenost (Φφselnou hodnotu).
void advance (InputIterator & iter, Distance
& n);
Pro iterßtory nßhodnΘho p°φstupu je to stejnΘ jako iter + n; nicmΘn∞
funkce je u₧iteΦnß, proto₧e operuje se vÜemi tvary iterßtor∙. Pro dop°ednΘ
iterßtory Distance musφ b²t kladnß, zatφmco pro iterßtory obousm∞rnΘ
a s nßhodn²m p°φstupem m∙₧e b²t kladnß i zßpornß. Funkce netestuje p°φpustnost
operace na p°ipojenΘm iterßtoru.
Druhß funkce distance vracφ poΦet operacφ iterßtoru nutn²ch
k p°esunu z jednoho prvku v sekvenci na jin². Popis funkce je:
void distance(InputIterator first, InputIterator
last, Distance &n);
V²sledek je vracen ve t°etφm parametru, kter² je p°edan² odkazem. Je
to hodnota urΦujφcφ, kolikrßt musφ b²t pou₧it operßtor ++ pro p°esun z
first
na last. Musφme se ujistit, ₧e prom∞nnß p°edanß tomuto parametru
je p°ed vyvolßnφm funkce sprßvn∞ inicializovanß.
-
N∞kterΘ algoritmy poskytnutΘ standardnφ knihovnou vy₧adujφ jako parametry
funkce. Jednoduch²m p°φkladem je algoritmus for_each, kter² vyvolßvß
funkci p°edanou jako parametr pro ka₧dou hodnotu dr₧enou v kontejneru.
Nßsledujφcφ k≤d nap°. aplikuje funkci printElement k vytvo°enφ v²stupu
popisujφcφho vÜechny prvky v seznamu celoΦφseln²ch hodnot:
void printElement (int value)
{
cout << "Seznam obsahuje
" << value << endl;
}
main ()
{
list<int> aList;
...
for_each (aList.begin(), aList.end(),
printElement);
}
Binßrnφ funkce p°ebφrajφ dva parametry a jsou Φasto aplikovßny na hodnoty
ze dvou r∙zn²ch sekvencφ. P°edpoklßdejme nap°. ₧e mßme seznam °et∞zc∙ a
seznam cel²ch Φφsel. Pro ka₧d² prvek v prvnφm seznamu chceme replikovat
°et∞zec tolikrßt, kolik udßvß odpovφdajφcφ hodnota ve druhΘm seznamu. Tuto
snadnou operaci m∙₧eme provΘst pomocφ funkce transform ze standardnφ
knihovny. Nejprve definujeme binßrnφ funkci s popsan²mi charakteristikami:
string stringRepeat (const string & base,
int number)
{
string result; //
p∙vodn∞ prßzdn² °et∞zec
while (number--) result
+= base;
return result;
}
Nßsledujφcφ volßnφ transform pak vytvß°φ po₧adovan² efekt:
list<string> words;
list<int> counts;
...
transform (words.begin(), words.end(),
counts.begin(), words.begin(),
stringRepeat);
Transformovßnφm slov one, two, three s hodnotami 3, 2, 3
dostaneme v²sledek oneoneone, twotwo, threethreethree.
-
Tvrzenφ jsou jednoduchΘ funkce, kterΘ vracejφ logickou nebo celoΦφselnou
hodnotu (true je nenula a false nula). Nßsledujφcφ funkce
p°ebφrß celΘ Φφslo a vracφ true pokud Φφslo reprezentuje p°estupn²
rok a false v opaΦnΘm p°φpad∞:
bool isLeapYear (unsigned int year)
{
if (0 == year % 400) return
true;
if (0 == year % 100) return
false;
if (0 == year % 4) return true;
return false;
}
Tvrzenφ se pou₧φvajφ jako parametry, nap°. v obecnΘm algoritmu find_if.
Tento algoritmus vracφ prvnφ hodnotu, kterß spl≥uje tvrzenφ nebo koncovou
hodnotu, pokud prvek nebyl nalezen. Pomocφ tohoto algoritmu nalezneme prvnφ
p°estupn² rok v seznamu rok∙:
list<int>::iterator firstLeap=find_if(aList.begin(),aList.end(),isLeapYear);
-
Objekt funkce je instance t°φdy, kterß definuje zßvorkov² operßtor
jako metodu. Je mnoho situacφ, kde lze pou₧φt objekt funkce na mφst∞ funkce.
Kdy₧ je pou₧it objekt funkce jako funkce, pak mφsto volßnφ funkce je pou₧it
zßvorkov² operßtor. Abychom si to mohli ukßzat, p°edpoklßdejme nßsledujφcφ
definici t°φdy:
class biggerThanThree {
public:
bool operator () (int val) {
return val > 3; }
};
Jestli₧e vytvo°φme instanci t°φdy biggerThanThree, pak poka₧dΘ
kdy₧ se odkazujeme na tento objekt pomocφ syntaxe volßnφ funkce, je vyvolßna
metoda zßvorkovΘho operßtoru. Nßsledujφcφm krokem je zobecn∞nφ tΘto t°φdy
p°idßnφm konstruktoru a konstantnφ datovΘ polo₧ky, kterß je konstruktorem
nastavena.
class biggerThan {
public:
const int
testValue;
biggerThan
(int x) : testValue(x) { }
bool operator
() (int val) { return val > testValue; }
};
V²sledkem je obecnΘ "v∞tÜφ ne₧ X", kde hodnota X je urΦena p°i vytvß°enφ
instance t°φdy. Nßsledujφcφ p°φklad nap°. hledß prvnφ hodnotu v seznamu,
kterß je v∞tÜφ ne₧ 12:
list<int>::iterator firstBig =
find_if (aList.begin(),
aList.end(), biggerThan(12));
T°i nejobecn∞jÜφ d∙vody pro pou₧φvßnφ objekt∙ funkcφ na mφst∞ normßlnφch
funkcφ jsou vyu₧itφ existujφcφho objektu funkce poskytnutΘho standardnφ
knihovnou mφsto novΘ funkce, vyvolßnφ provßd∞nΘ pomocφ vno°enΘ funkce a
umo₧n∞nφ objektu funkce zp°φstup≥ovat nebo nastavovat stavovΘ informace
dr₧enΘ objektem. Pozd∞ji se seznßmφme s p°φklady.
Nßsledujφcφ tabulka ukazuje objekty funkcφ poskytnutΘ standardnφ knihovnou.
JmΘno
|
Implementovanß operace
|
AritmetickΘ funkce |
|
plus |
sΦφtßnφ x + y |
minus |
odeΦφtßnφ x - y |
multiplies |
nßsobenφ x * y |
divides |
d∞lenφ x / y |
modulus |
zbytek po d∞lenφ x % y |
negate |
negace - x |
Porovnßvacφ funkce |
|
equal_to |
test rovnosti x == y |
not_equal_to |
test nerovnosti x != y |
greater |
v∞tÜφ x > y |
less |
menÜφ x < y |
greater_equal |
v∞tÜφ nebo rovno x >= y |
less_equal |
menÜφ nebo rovno x <= y |
LogickΘ funkce |
|
logical_and |
logick² souΦin x && y |
logical_or |
logick² souΦet x || y |
logical_not |
logickß negace ! x |
Nßsleduje °ada p°φklad∙, kterΘ ukazujφ pou₧φvßnφ objekt∙ funkcφ. Prvnφ
p°φklad pou₧φvß plus pro v²poΦet souΦt∙ dvou seznam∙ cel²ch Φφsel
po prvcφch a umφst∞nφ v²sledk∙ zp∞t do prvnφho seznamu. To m∙₧e b²t provedeno
takto:
transform (listOne.begin(), listOne.end(),
listTwo.begin(),
listOne.begin(), plus<int>()
);
Druh² p°φklad neguje ka₧d² prvek ve vektoru logick²ch hodnot:
transform (aVec.begin(), aVec.end(), aVec.begin(),
logical_not<bool>() );
Zßkladnφ t°φdy pou₧itΘ standardnφ knihovnou v definicφch funkcφ uveden²ch
v p°edchozφ tabulce jsou dostupnΘ pro vytvß°enφ nov²ch objekt∙ unßrnφch
a binßrnφch funkcφ. Zßkladnφ t°φdy jsou definovßny takto:
template <class Arg, class Result>
struct unary_function {
typedef Arg argument_type;
typedef Result result_type;
};
template <class Arg1, class Arg2, class
Result>
struct binary_function {
typedef Arg1 first_argument_type;
typedef Arg2 second_argument_type;
typedef Result result_type;
};
Chceme nap°. p°evzφt binßrnφ funkcφ parametr typu Widget a celΘ
Φφslo a porovnat identifikaΦnφ Φφslo "widgetu" s p°edanou hodnotou. Funkci,
kterß toto provßdφ zapφÜeme takto:
struct WidgetTester : binary_function<Widget,
int, bool> {
public:
bool operator () (const Widget
& wid, int testid) const
{ return wid.id
== testid; }
};
Druh²m d∙vodem pro pou₧φvßnφ objekt∙ funkcφ namφsto funkcφ je rychlejÜφ
k≤d. V mnoha p°φpadech vyvolßnφ objektu funkce, jako je p°φklad volßnφ
funkce transform uveden² v²Üe, m∙₧e b²t provedeno jako vlo₧enß funkce,
co₧ eliminuje volßnφ p°ekrytΘ funkce.
T°etφm hlavnφm d∙vodem pou₧itφ objektu funkce na mφst∞ funkce je to,
₧e ka₧dΘ volßnφ m∙₧e zjistit n∞jak² stav nastaven² p°edchozφm volßnφm.
P°φkladem je vytvß°enφ generßtoru pou₧itΘho v obecnΘm algoritmu generate.
Generßtor je jednoduchß funkce, kterß vracφ p°i ka₧dΘm vyvolßnφ jinou hodnotu.
╚asto pou₧φvan²m tvarem generßtoru je generßtor nßhodn²ch Φφsel, ale jsou
takΘ jinß pou₧itφ pro tuto koncepci. SekvenΦnφ generßtor jednoduÜe vracφ
hodnoty zv∞tÜujφcφ se sekvence cel²ch Φφsel (1, 2, 3, 4 atd.). Toto provßdφ
t°φda iotaGen, kterß je definovßna takto:
class iotaGen {
public:
iotaGen (int start = 0) : current(start)
{ }
int operator () () { return
current++; }
private:
int current;
};
Objekt udr₧uje souΦasnou hodnotu, kterß m∙₧e b²t nastavena konstruktorem
(nebo implicitn∞ na 0). Poka₧dΘ kdy₧ je pou₧it operßtor funkΦnφho volßnφ,
pak je vrßcena souΦasnß hodnota a je takΘ inkrementovßna. Pomocφ tohoto
objektu v nßsledujφcφm volßnφ standardnφ knihovnφ funkce generate
inicializujeme vektor 20 prvk∙ hodnotami 1 a₧ 20:
vector<int> aVec(20);
generate (aVec.begin(), aVec.end(), iotaGen(1));
-
FunkΦnφ adaptΘr je instance t°φdy, kterß adaptuje globßlnφ funkci
nebo metodu tak, ₧e funkce m∙₧e b²t pou₧ita jako objekt funkce (funkΦnφ
adaptΘry mohou b²t takΘ pou₧ity ke zm∞n∞ chovßnφ funkce nebo objektu funkce).
Ka₧d² funkΦnφ adaptΘr poskytuje konstruktor, kter² p°ebφrß globßlnφ funkci
nebo metodu. AdaptΘr takΘ poskytuje zßvorkov² operßtor, kter² sm∞ruje svΘ
volßnφ na tuto p°i°azenou globßlnφ funkci nebo metodu.
èablony pointer_to_unary_function a pointer_to_binary_function
adaptujφ jedno nebo dvou parametrovΘ globßlnφ funkce. Tyto adaptΘry mohou
b²t aplikovßny p°φmo nebo m∙₧eme pou₧φt Üablonu funkce ptr_fun k
vytvo°enφ p°φsluÜnΘho adaptΘru automaticky. Nap°. m∙₧eme adaptovat jednoduchou
funkci times3 a aplikovat ji na vektor cel²ch Φφsel takto:
int times3(int x) {
return 3*x;
}
int a[] = {1,2,3,4,5};
vector<int> v(a,a+5), v2;
transform(v.begin(),v.end(),v2.end(),ptr_fun(times3));
P°φpadn∞ m∙₧eme aplikovat adaptΘr, a pak p°edat nov² adaptovan² objekt
funkce vektoru.
pointer_to_unary_function<int,int> pf(times3);
transform(v.begin(),v.end(),v2.end(),pf);
Rodina Üablon mem_fun adaptuje metody mφsto globßlnφch funkcφ.
Nap°. pokud mßme mno₧inu seznam∙ a chceme ka₧d² seznam z mno₧iny se°adit,
pak m∙₧eme pou₧φt mem_fun_t (nebo jednoduÜÜφ mem_fun) k aplikovßnφ
metody °azenφ seznamu na ka₧d² prvek v mno₧in∞.
set<list<int>* > s;
// Inicializace mno₧iny seznamy
à
// Se°azenφ ka₧dΘho seznamu v mno₧in∞.
for_each(s.begin(),s.end(),mem_fun(&list<int>::sort));
To je nezbytnΘ, nebo¥ obecn² °adφcφ algoritmus nem∙₧e b²t pou₧it pro
seznam. Je to takΘ nejjednoduÜÜφ zp∙sob p°φstupu k libovoln²m polymorfick²m
charakteristikßm objekt∙ dr₧en²ch ve standardnφm kontejneru. Nap°. m∙₧eme
vyvolat virtußlnφ kreslφcφ funkce na kolekci objekt∙, kterΘ jsou Φßstφ
hierarchie tvar∙, jako zde:
// hierarchie tvar∙
class shape {
virtual void draw();
};
class circle : public shape {
void draw();
};
class square : public shape {
void draw();
};
// Vytvo°enφ vektoru tvar∙
circle c;
square s;
vector<shape*> v;
v.push_back(&s);
v.push_back(&c);
// Volßnφ draw pro vÜechny
for_each(v.begin(),v.end(), mem_fun(&shape::draw));
Podobn∞ jako adaptΘry globßlnφch funkcφ i adaptΘry metod obsahujφ Üablonu
t°φd a p°i°azenou Üablonu funkce. T°φda je aktußlnφ adaptΘr, zatφmco funkce
zjednoduÜuje pou₧itφ t°φdy vytvo°enφm instance tΘto t°φdy. Nap°. v nßsledujφcφm
p°φklad∞ vytvß°φme mem_fun_t sami, a pak ji p°edßme algoritmu for_each:
mem_fun_t<shape> mf(&shape::draw);
for_each(v.begin(),v.end(),mf);
M∙₧eme takΘ vid∞t, ₧e Üablona funkce mem_fun zjednoduÜuje pou₧itφ
adaptΘru mem_fun_t umo₧n∞nφm poΦφtaΦi vytvo°it typy po₧adovanΘ mem_fun_t.
Knihovna poskytuje metody adaptΘr∙ bez parametr∙ a s jednφm parametrem.
To m∙₧e zjednoduÜit Üφ°enφ metod s vφce parametry.
-
Negßto°i a spojovaΦi jsou funkce adaptΘr∙, kterΘ jsou pou₧ity
k budovßnφ nov²ch objekt∙ funkcφ mimo existujφcφ objekty funkcφ. V∞tÜinou
jsou aplikovßny na funkce jako Φßst procesu budovßnφ seznamu parametr∙
p°ed vyvolßnφm jinΘ funkce nebo obecnΘho algoritmu.
Negßtory not1 a not2 p°ebφrajφ unßrnφ a binßrnφ objekt
funkce tvrzenφ a vytvß°ejφ nov² objekt funkce, kter² je dopl≥kem originßlu.
Nap°. pou₧ijeme-li objekt funkce definovan² v p°edchozφm bod∞, pak objekt
funkce
not2(WidgetTester())
poskytuje binßrnφ tvrzenφ, kterΘ p°ebφrß p°esn∞ stejnΘ parametry jako
WidgetTester
a mß znegovan² v²sledek. Negßto°i pracujφ pouze s objekty funkce definovan²mi
jako podt°φdy t°φd unary_function a binary_function.
SpojovaΦ p°ebφrß dvouparametrovou funkci a spojuje parametr urΦujφcφ
funkci s parametrem obsahujφcφm hodnotu, tj. vytvß°φ jednoparametrovou
funkci. Funkce musφ b²t podt°φdou t°φdy binary_function. SpojovaΦ
bind1st spojuje prvnφ parametr zatφmco spojovaΦ bind2nd spojuje
druh².
Nap°. spojovaΦ bind2nd(greater<int>(), 5) vytvß°φ objekt
funkce, kter² testuje zda hodnota je v∞tÜφ ne₧ 5. M∙₧eme ji pou₧φt k vytvo°enφ
iterßtoru reprezentujφcφho prvnφ hodnotu v seznamu v∞tÜφ ne₧ 5:
list<int>::iterator where = find_if(aList.begin(),
aList.end(),
bind2nd(greater<int>(), 5));
Kombinacφ spojovaΦe a negßtoru m∙₧eme vytvo°it funkci, kterß mß hodnotu
true,
pokud parametr je d∞liteln² 3 a false v opaΦnΘm p°φpad∞. To m∙₧eme
pou₧φt k odstran∞nφ vÜech nßsobk∙ 3 ze seznamu:
list<int>::iterator where = remove_if
(aList.begin(), aList.end(),
not1(bind2nd(modulus<int>(), 3)));
-
Standardnφ knihovna poskytuje 10 alternativnφch forem kontejner∙. V tΘto
sekci struΦn∞ popφÜeme jejich charakteristiky. V nßsledujφcφch sekcφch
pak jsou popsßny jednotlivΘ typy kontejner∙ podrobn∞ji. Charakteristiky
kontejner∙ jsou uvedeny v nßsledujφcφ tabulce.
JmΘno
|
Charakteristika
|
vector |
nßhodn² p°φstup k prvk∙m, efektivnφ vklßdßnφ na konec |
list |
efektivnφ vklßdßnφ a odstra≥ovßnφ kdekoliv |
deque |
nßhodn² p°φstup, efektivnφ vklßdßnφ na zaΦßtek a konec |
set |
prvky udr₧ovßny v po°adφ, efektivnφ testovßnφ obsahu, vklßdßnφ a odstra≥ovßnφ |
multiset |
mno₧ina s opakovan²mi prvky |
map |
p°φstup k hodnotßm prost°ednictvφm klφΦ∙, efektivnφ vklßdßnφ a odstra≥ovßnφ |
multimap |
map umo₧≥ujφcφ duplicitnφ klφΦe |
stack |
vklßdßnφ a odstra≥ovßnφ pouze na vrcholu |
queue |
vklßdßnφ na konec, odstra≥ovßnφ ze zaΦßtku |
priority queue |
efektivnφ p°φstup a odstran∞nφ nejv∞tÜφ hodnoty |
Kontejnery ve standardnφ knihovn∞ mohou udr₧ovat r∙znΘ typy prvk∙. Zahrnujφ
zßkladnφ datovΘ typy (int, char apod.), ukazatele nebo u₧ivatelem
definovanΘ typy. Kontejnery nemohou obsahovat odkazy. Obecn∞ sprßva pam∞ti
je provßd∞na automaticky standardnφmi t°φdami kontejner∙ s malou spolupracφ
programßtora.
Hodnoty jsou umφs¥ovßny do kontejneru kopφrovacφm konstruktorem. Pro
v∞tÜinu t°φd kontejner∙ musφ typ prvku dr₧en² kontejnerem takΘ definovat
implicitnφ konstruktor. Obecn² algoritmus, kter² kopφruje do kontejneru
(jako je copy) pou₧φvß operßtor p°i°azenφ.
Kdy₧ je cel² kontejner duplikovßn (nap°. vyvolßnφm kopφrovacφho konstruktoru
nebo jako v²sledek p°i°azenφ), pak ka₧dß hodnota je p°ekopφrovßna do novΘ
struktury (v zßvislosti na struktu°e) operßtorem p°i°azenφ nebo kopφrovacφm
konstruktorem. Pam∞¥ pro struktury pou₧itß intern∞ r∙zn²mi t°φdami kontejner∙
je alokovßna a uvol≥ovßna automaticky a efektivn∞.
Pokud pro typ prvku je definovßn destruktor, pak tento destruktor je
vyvolßvßn p°i odstra≥ovßnφ hodnot z kontejneru. P°i ruÜenφ celΘ kolekce,
je destruktor vyvolßvßn, pro ka₧dou hodnotu dr₧enou v kontejneru.
N∞kolik slov si °ekneme i o kontejnerech, kterΘ dr₧φ ukazatele. Nap°.
kolekce ukazatel∙ je pouze zp∙sob ulo₧enφ hodnot, kterΘ m∙₧eme reprezentovat
instancemi t°φdy nebo instancemi podt°φdy. V t∞chto p°φpadech je kontejner
zodpov∞dn² pouze za udr₧ovßnφ samotn²ch ukazatel∙. Za sprßvu pam∞ti, na
kterou ukazatele ukazujφ, zodpovφdß programßtor. To zahrnuje alokovßnφ
(pomocφ operßtoru new) a na zßv∞r p°ed odstran∞nφm ukazatele z kontejneru,
uvoln∞nφ prvku.
Je n∞kolik klasick²ch kontejner∙, kterΘ nenajdeme ve standardnφ knihovn∞.
Ve v∞tÜin∞ p°φpad∙ je d∙vod ten, ₧e kontejner, kter² mß b²t poskytnut,
m∙₧e b²t snadno vytvo°en. Nemßme zde nap°. kontejner stromu. Datov² typ
set je intern∞ implementovßn pomocφ binßrnφho vyhledßvacφho stromu.
Pro mnoho problΘm∙ kterΘ vy₧adujφ pou₧itφ strom∙ je datov² typ set
adekvßtnφ nßhradou. Datov² typ set je specißln∞ °azen a nenφ urΦen
pro provßd∞nφ mno₧inov²ch operacφ na kolekci hodnot, kterΘ nemohou b²t
°azeny (nap°. na mno₧in∞ komplexnφch Φφsel). V t∞chto p°φpadech jako nßhradu
lze pou₧φt seznam, i kdy₧ zde budeme muset zapsat funkce pro specißlnφ
mno₧inovΘ operace.
Nejsou zde takΘ vφcerozm∞rnß pole, ale vektor m∙₧e obsahovat jinΘ vektory
jako prvky a tak po₧adovanß struktura m∙₧e b²t snadno vytvo°ena.
Knihovna neobsahuje takΘ grafy. Jedna reprezentace grafu m∙₧e b²t snadno
vytvo°ena typem map, kter² dr₧φ dalÜφ map. Nejsou zde takΘ
°φdkß pole. ╪eÜenφm tohoto problΘmu je pou₧itφ grafovΘ reprezentace. TakΘ
nemßme heÜovacφ tabulky. M∙₧eme je snadno vytvo°it jako vector (nebo
deque),
kter² dr₧φ seznamy nebo mno₧iny jako svΘ prvky.
-
T°φda kontejneru vector zobec≥uje koncepci pole jazyka C. Podobn∞
jako pole i vector je indexovanß datovß struktura, s hodnotami index∙
od 0 do poΦtu prvk∙ obsa₧en²ch ve struktu°e zmenÜenΘ o 1. TakΘ jako u pole,
hodnoty prvk∙ mohou b²t p°i°azovßny a zφskßvßny pomocφ operßtoru indexace.
Vektor se ale liÜφ od pole v nßsledujφcφch d∙le₧it²ch v∞cech:
-
Vektor znß sßm sebe vφce ne₧ normßlnφ pole. Vektoru se m∙₧eme ptßt na jeho
kapacitu, na poΦet prvk∙, kterΘ prßv∞ dr₧φ (m∙₧e se liÜit od jeho souΦasnΘ
kapacity) apod.
-
Velikost vektoru se m∙₧e m∞nit dynamicky. NovΘ prvky mohou b²t p°idßvßny
na konec vektoru nebo doprost°ed (mΘn∞ efektivnφ). Sprßva uklßdßnφ je efektivnφ
a automatickß. Pokud pou₧φvßme mnoho vklßdacφch operacφ, pak je v²hodn∞jÜφ
pou₧φt kontejner seznamu.
T°φdu kontejneru vector ve standardnφ knihovn∞ m∙₧eme porovnat s
t°φdou kontejneru deque. Jako vektor i deque je indexovanß
datovß struktura. Hlavnφ rozdφl je ten, ₧e deque poskytuje efektivnφ
vklßdßnφ na zaΦßtek a konec kontejneru, zatφmco vektor poskytuje efektivnφ
vklßdßnφ pouze na konec. V mnoha situacφch mohou b²t pou₧ity ob∞ struktury.
P°i pou₧itφ vektoru obecn∞ dostaneme menÜφ spustiteln² soubor, zatφmco
v zßvislosti na mno₧in∞ pou₧φvan²ch operacφ, pou₧itφ deque m∙₧e
produkovat rychlejÜφ program.
Kdy₧ pou₧φvßme vektor, pak do programu musφme vlo₧it hlaviΦkov² soubor
vector:
# include <vector>
-
Nynφ se budeme zab²vat popisem metod datovΘho typy vektor. Proto₧e se jednß
o Üablonu t°φdy, deklarace vektoru musφ zahrnovat urΦenφ typu prvk∙ kontejneru.
M∙₧e to b²t primitivnφ typ (jako int nebo
double), typ ukazatel
nebo u₧ivatelem definovan² typ. V poslednφm p°φpad∞, u₧ivatelem definovan²
typ musφ implementovat implicitnφ konstruktor (bude pou₧it k inicializaci
nov∞ vytvo°en²ch prvk∙). Kopφrovacφ konstruktor (definovan² explicitn∞
nebo implicitn∞) musφ takΘ existovat pro typ prvku kontejneru. Podobn∞
jako pole, i vektor je Φasto deklarovßn s celoΦφseln²m parametrem, kter²
urΦuje poΦet prvk∙ vektoru:
vector<int> vec_one(10);
Konstruktor pou₧it² v tΘto situaci k vytvo°enφ vektoru je deklarovßn
jako explicitnφ, co₧ zabra≥uje, aby byl pou₧it jako konverznφ operßtor
(obecn∞ je to vhodnΘ, nebo¥ jinak celΘ Φφslo m∙₧e b²t ne°φzen∞ p°evedeno
v jist²ch situacφch na vektor).
Konstruktor pou₧φvan² k vytvß°enφ vektor∙ m∙₧e mφt r∙znΘ tvary. Mimo
velikosti, konstruktor m∙₧e p°ebφrat konstantnφ hodnotu, kterß bude pou₧ita
k inicializaci vÜech prvk∙ pole. Pokud nenφ poskytnuta velikost, pak vektor
zpoΦßtku neobsahuje ₧ßdn² prvek a je zv∞tÜovßn automaticky p°i p°idßvßnφ
prvku. Kopφrovacφ konstruktor vytvß°φ klon vektoru z jinΘho vektoru.
vector<int> vec_two(5, 3);
// kopφrovacφ konstruktor
vector<int> vec_three;
vector<int> vec_four(vec_two); //
inicializace p°i°azenφm
Vektor m∙₧e b²t takΘ inicializovßn pomocφ prvk∙ z jinΘ kolekce zadßnφm
poΦßteΦnφho a koncovΘho iterßtoru. Parametry mohou b²t libovoln²m tvarem
iterßtoru, tedy kolekce m∙₧e b²t inicializovßna hodnotami z libovolnΘ t°φdy
kontejneru ve standardnφ knihovn∞ kterß podporuje iterßtory.
vector <int> vec_five (aList.begin(),
aList.end());
Vektor m∙₧e p°i°adit hodnoty jinΘmu vektoru (je p°i°azena kopie vektoru):
vec_three = vec_five;
Metoda assign se podobß p°i°azenφ, ale je mnohostrann∞jÜφ a
v n∞kter²ch p°φpadech vy₧aduje vφce parametr∙. Podobn∞ jako u p°i°azenφ,
existujφcφ hodnoty v kontejneru jsou zruÜeny a nahrazeny hodnotami urΦen²mi
parametry. Jsou dva tvary assign. Prvnφ p°ebφrß dva parametry (iterßtory),
kterΘ specifikujφ sekvenci v existujφcφm kontejneru. Hodnoty z tΘto sekvence
jsou p°i°azeny. Druhß verze assign p°ebφrß poΦet a volitelnou hodnotu
typu prvk∙ kontejneru. Po ukonΦenφ volßnφ, bude kontejner obsahovat pouze
poΦet prvk∙ urΦen²ch prvnφm parametrem, kterΘ jsou rovny implicitnφ hodnot∞
pro typ prvku kontejneru nebo specifikovanΘ poΦßteΦnφ hodnot∞ (p°φpadn²
druh² parametr).
vec_six.assign(list_ten.begin(), list_ten.end());
vec_four.assign(3, 7);
// t°i kopie hodnoty 7
vec_five.assign(12);
// dvanßct kopiφ hodnoty 0
Pokud je definovßn destruktor pro typ prvku kontejneru, pak destruktor
bude volßn pro ka₧dou hodnotu odstra≥ovanou z kolekce.
Dva vektory takΘ mohou zam∞nit svΘ celΘ obsahy pomocφ operace swap.
Kontejner parametru p°evezme hodnoty kontejneru objektu a naopak.
vec_three.swap(vec_four);
T°φda vector obsahuje n∞kolik definicφ typ∙. Jsou Φasto pou₧φvßny
v deklaraΦnφch p°φkazech. Nap°. iterßtor pro vektor cel²ch Φφsel m∙₧e b²t
deklarovßn takto:
vector<int>::iterator location;
Mimo iterator jsou definovßny nßsledujφcφ typy:
value_type |
Typ prvku vektoru. |
const_iterator |
Iterßtor, kter² neumo₧≥uje modifikaci p°ipojenΘ sekvence. |
reverse_iterator |
Iterßtor, kter² se p°esouvß sm∞rem dozadu. |
const_reverse_iterator |
Kombinace konstantnφho a reverznφho iterßtoru. |
reference |
Odkaz na p°ipojen² prvek. |
const_reference |
Odkaz na p°ipojen² prvek, kter² neumo₧≥uje modifikaci prvku. |
size_type |
Typ pou₧it² k odkazovßnφ na velikost kontejneru. |
difference_type |
Typ pou₧it² k ulo₧enφ vzdßlenosti mezi iterßtory. |
allocator_type |
Typ alokßtoru pou₧itΘho pro sprßvu pam∞ti pro vektor. |
Stejn∞ jako u pole i u vektoru se pro p°φstup a modifikaci jednotliv²ch
prvk∙ vektoru pou₧φvß operßtor indexace. V souΦasnΘ verzi nenφ mo₧no ov∞°it
p°φpustnost hodnoty indexu (m∙₧e se v nßsledujφcφch verzφch zm∞nit). Indexace
konstantnφho vektoru dßvß konstantnφ odkaz. Pokus o indexaci vektoru mimo
rozsah p°φpustn²ch hodnot generuje nep°edvφdateln² a chybn² v²sledek:
cout << vec_five[1] << endl;
vec_five[1] = 17;
Metoda at m∙₧e b²t pou₧ita mφsto operßtoru indexace. P°ebφrß
stejnΘ parametry jako operßtor indexace a vracφ p°esn∞ stejn² v²sledek.
Metoda front vracφ prvnφ prvek ve vektoru, zatφmco metoda back
poskytuje poslednφ prvek. Ob∞ takΘ vracejφ konstantnφ odkazy, kdy₧ jsou
aplikovßny na konstantnφ vektory.
cout << vec_five.front() << "
... " << vec_five.back() << endl;
Obecn∞ jsou t°i r∙znΘ velikosti p°i°azenΘ k libovolnΘmu vektoru. Prvnφm
je poΦet prvk∙ souΦasn∞ dr₧en²ch vektorem. Druh²m je maximßlnφ velikost,
na kterou vektor se m∙₧e rozÜφ°it bez po₧adavk∙ na alokovßnφ novΘho mφsta.
T°etφm je hornφ limit velikosti libovolnΘho vektoru. Tyto t°i hodnoty zφskßme
metodami size, capacity a max_size.
cout << "velikost: " << vec_five.size()
<< endl;
cout << "kapacita: " << vec_five.capacity()
<< endl;
cout << "max_velikost: " << vec_five.max_size()
<< endl;
Maximßlnφ velikost je obvykle omezena pouze dostupnou pam∞tφ nebo nejv∞tÜφ
hodnotou, kterß m∙₧e b²t ulo₧ena v datovΘm typu size_type. Charakterizovat
souΦasnou velikost a kapacitu je slo₧it∞jÜφ. Jak jsme se ji₧ dozv∞d∞li,
prvky mohou b²t p°idßvßny do a odstra≥ovßny z vektoru r∙zn²mi zp∙soby.
Kdy₧ prvky jsou z vektoru odstran∞ny, pak pam∞¥ pro vektor obvykle nenφ
realokovßna a tedy velikost se zmenÜφ, ale kapacita z∙stßvß stejnß. ╪ada
vklßdßnφ nevy₧aduje realokaci novΘ pam∞ti, pokud p∙vodnφ kapacita nenφ
p°ekroΦena.
Vklßdßnφ, kterΘ zp∙sobφ, ₧e velikost p°ekroΦφ kapacitu obecn∞ zp∙sobφ
alokovßnφ novΘho bloku pam∞ti pro ulo₧enφ prvk∙ vektoru. Hodnoty jsou pak
kopφrovßny do tΘto novΘ pam∞ti pomocφ operßtoru p°i°azenφ a starß pam∞¥
je zruÜena. Proto₧e to m∙₧e b²t potencißln∞ nßkladnß operace, datov² typ
vector
umo₧≥uje programßtoru specifikovat hodnotu kapacity vektoru. Metoda reserve
je direktiva pro vektor, indikujφcφ, ₧e vektor mß b²t zv∞tÜen alespo≥ na
danou velikost. Pokud parametr reserve je v∞tÜφ ne₧ souΦasnß kapacita,
pak nastane realokace a hodnota parametru urΦuje novou kapacitu. Pokud
kapacita ji₧ p°ekraΦuje hodnotu parametru, pak k ₧ßdnΘ realokaci nedojde.
Vyvolßnφ reserve nem∞nφ velikost vektoru ani hodnoty samotn²ch prvk∙.
vec_five.reserve(20);
Realokace znehodnotφ vÜechny odkazy, ukazatele a iterßtory odkazujφcφ
se na prvky dr₧enΘ vektorem.
Metoda empty vracφ true, pokud souΦasnß velikost vektoru
je nula (bez ohledu na kapacitu vektoru). Pou₧itφ tΘto metody je obecn∞
efektivn∞jÜφ ne₧ porovnßvßnφ vrßcenΘ velikosti s nulou.
cout << "je prßzdn² " << vec_five.empty()
<< endl;
Metoda resize m∞nφ velikost vektoru na hodnotu specifikovanou
parametrem. Hodnoty mohou b²t p°idßny na nebo zruÜeny z konce kolekce podle
pot°eby. Voliteln² druh² parametr m∙₧e b²t pou₧it pro poskytnutφ poΦßteΦnφ
hodnoty nov²ch prvk∙ p°idan²ch do kolekce. Pokud je definovßn destruktor
pro typ prvku, pak je vyvolßvßn pro vÜechny hodnoty odstra≥ovanΘ z kolekce.
// velikost bude 12 a v p°φpad∞ pot°eby budou
p°idßny hodnoty 17
vec_five.resize (12, 17);
Jak ji₧ bylo uvedeno v²Üe, t°φda vector se liÜφ od b∞₧nΘho pole
tφm, ₧e vektor m∙₧e v jist²ch situacφch zv∞tÜovat nebo zmenÜovat svou velikost.
Kdy₧ vklßdßnφ zp∙sobφ, ₧e poΦet prvk∙ dr₧en²ch ve vektoru p°ekroΦφ kapacitu
vektoru, pak je alokovßn nov² blok a prvky jsou p°ekopφrovßny na novΘ mφsto.
Nov² prvek m∙₧e b²t p°idßn na konec vektoru pomocφ metody push_back.
Pokud v souΦasnΘ alokaci je mφsto, pak tato operace je velmi efektivnφ.
vec_five.push_back(21); // p°idßnφ
prvku 21 na konec vektoru
Odpovφdajφcφ odstra≥ovacφ operace je pop_back, kterß zmenÜuje
velikost vektoru, ale nem∞nφ jeho kapacitu. Tato operace je op∞t znaΦn∞
efektivnφ.
Obecn∞jÜφ vklßdacφ operace m∙₧e b²t provßd∞na metodou insert.
Mφsto vlo₧enφ je popsßno iterßtorem, vlo₧enφ prob∞hne bezprost°edn∞ p°ed
urΦenΘ mφsto. Pevn² poΦet stejn²ch prvk∙ m∙₧e b²t vlo₧en jednφm volßnφm.
Je efektivn∞jÜφ vklßdßnφ bloku prvk∙ jednφm volßnφm ne₧ provßd∞nφ °ady
jednotliv²ch vklßdßnφ, proto₧e jedno volßnφ provede nejv²Üe jednu alokaci.
// nalezenφ 7
vector<int>::iterator where = find(vec_five.begin(),
vec_five.end(), 7);
vec_five.insert(where, 12);
// vlo₧enφ 12 p°ed 7
vec_five.insert(where, 6, 14); // vlo₧enφ
Üest kopiφ 14
Obecn∞jÜφ tvar metody insert p°ebφrß pozici a dvojici iterßtor∙,
kterΘ urΦujφ sekvenci z jinΘho kontejneru. Rozsah hodnot popsan²ch sekvencφ
je vlo₧en do vektoru. Pou₧itφ tΘto funkce je v²hodn∞jÜφ ne₧ provedenφ °ady
jednotliv²ch vklßdßnφ.
vec_five.insert (where, vec_three.begin(),
vec_three.end());
Mimo metody pop_back, kterß odstra≥uje prvek z konce vektoru,
existuje takΘ metoda odstra≥ujφcφ prvky z prost°edku vektoru, kde pozice
odstra≥ovanΘho prvku je urΦena iterßtorem. Jednß se o metodu
erase.
Metoda mß dva tvary: prvnφ p°ebφrß jeden iterßtor a odstra≥uje jednu hodnotu,
zatφmco druh² p°ebφrß dvojici iterßtor∙ a odstra≥uje hodnoty v danΘm rozsahu.
Velikost vektoru je zmenÜena, ale kapacita se nem∞nφ.
vec_five.erase(where);
// zruÜenφ od hodnoty 12 do konce
where = find(vec_five.begin(), vec_five.end(),
12);
vec_five.erase(where, vec_five.end());
Metody begin a end vytvß°ejφ iterßtory nßhodnΘho
p°φstupu pro kontejner. Pamatujte, ₧e iterßtory z°φzenΘ t∞mito operacemi
mohou b²t znehodnoceny vlo₧enφm nebo odstran∞nφm prvk∙. Metody rbegin
a rend vracejφ podobnΘ iterßtory, kterΘ ale umo₧≥ujφ p°istupovat
k prvk∙m v opaΦnΘm po°adφ. Konstantnφ iterßtory jsou vrßceny, pokud kontejner
je p∙vodn∞ deklarovßn jako konstantnφ nebo pokud cφl p°i°azenφ nebo parametr
je konstantnφ.
Vektor neposkytuje ₧ßdnou metodu, kterß m∙₧e b²t p°φmo pou₧ita k urΦenφ
zda specifickß hodnota je obsa₧ena v kolekci. Pro tento ·Φel m∙₧e b²t ale
pou₧it obecn² algoritmus find nebo count. Nßsledujφcφ p°φkaz
nap°. testuje zda celoΦφseln² vektor obsahuje hodnotu 17:
int num = 0;
count (vec_five.begin(), vec_five.end(),
17, num);
if (num) cout << "obsahuje 17" <<
endl;
else cout << "neobsahuje 17" <<
endl;
Vektor automaticky neudr₧uje svΘ hodnoty v rostoucφ sekvenci. M∙₧e
b²t ale uveden do po°adφ pomocφ obecnΘho algoritmu sort. JednoduÜÜφ
forma °azenφ pou₧φvß pro svΘ porovnßvßnφ operßtor menÜφ ne₧ pro typ prvku.
Alternativnφ verze obecnΘho algoritmu po₧aduje od programßtora explicitnφ
specifikaci porovnßvacφho operßtoru. Ten pak m∙₧e b²t pou₧it nap°. k umφst∞nφ
prvk∙ v sestupnΘm namφsto vzestupnΘho po°adφ:
sort (aVec.begin(), aVec.end()); //
vzestupnΘ °azenφ
sort (aVec.begin(), aVec.end(), greater<int>()
); // specifikace operßtoru
sort (aVec.rbegin(), aVec.rend()); // alternativnφ
zp∙sob sestupnΘho °azenφ
Mnoho z obecn²ch algoritm∙ m∙₧e b²t pou₧ito s vektory. Jsou uvedeny
v nßsledujφcφ tabulce. Nap°. maximßlnφ hodnota vektoru m∙₧e b²t urΦena
takto:
vector<int>::iterator where = max_element(vec_five.begin(),
vec_five.end());
cout << "maximum je " << *where
<< endl;
╚innost |
JmΘno |
Plnφ vektor dan²mi poΦßteΦnφmi hodnotami |
fill |
Kopφruje jednu sekvenci do druhΘ |
copy |
Kopφruje hodnoty z generßtoru do vektoru |
generate |
Hledß prvek spl≥ujφcφ podmφnku |
find |
Hledß p°φpadnΘ duplicitnφ prvky |
adjacent_find |
Hledß podsekvenci ve vektoru |
search |
Lokalizuje maximßlnφ nebo minimßlnφ prvek |
max_element, min_element |
Obracφ po°adφ prvk∙ |
reverse |
Nahrazuje prvky nov²mi hodnotami |
replace |
Rotuje prvky okolo st°edu |
rotate |
Rozd∞luje prvky do skupin |
partition |
Generovßnφ permutacφ |
next_permutation |
Spojovßnφ vektor∙ |
inplace_merge |
NßhodnΘ zapln∞nφ prvk∙ vektoru |
random_shuffle |
PoΦet prvk∙ spl≥ujφcφch podmφnku |
count |
Redukce vektoru na jednu hodnotu |
accumulate |
Vnit°nφ produkt vektor∙ |
inner_product |
Test dvou vektor∙ na rovnost pßr∙ |
equal |
LexikografickΘ porovnßvßnφ |
lexicographical_compare |
Aplikovßnφ transformace na vektor |
transform |
╚ßsteΦnΘ souΦty hodnot |
partial_sum |
Rozdφly sousednφch hodnot |
adjacent_difference |
Provedenφ funkce pro ka₧d² prvek |
for_each |
-
Vektory bit∙ (hodnot 0/1) jsou zpracovßvßny standardnφ knihovnou specißlnφm
zp∙sobem, kdy hodnoty jsou efektivn∞ pakovßny (n∞kolik prvk∙ do slova).
Operace pro logick² vektor vector<bool> jsou nadmno₧inou operacφ
normßlnφho vektoru. Novß p°idanß metoda pro logick² vektor je flip.
Jejφm vyvolßnφm invertujeme vÜechny bity vektoru. LogickΘ vektory takΘ
vracejφ jako odkaz internφ hodnotu, kterou takΘ podporuje metoda flip.
vector<bool> bvec(27);
bvec.flip();
// inverze vÜech prvk∙
bvec[17].flip();
// inverze bitu 17
Logick² vektor takΘ podporuje metodu swap, kterß umo₧≥uje vym∞niti
hodnoty urΦenΘ dvojicφ odkaz∙:
bvec.swap(bvec [17], bvec [16]);
-
P°φklad programu, kter² ukazuje pou₧itφ vektor∙ je klasick² algoritmus
nazvan² Eratosovo sφto, pou₧φvan² k urΦenφ prvoΦφsel. Seznam vÜech Φφsel
pat°φcφch do n∞jakΘho intervalu je reprezentovßn celoΦφseln²m vektorem.
Zßkladnφ myÜlenkou je vy°azenφ (nastavenφm na 0) t∞ch hodnot, kterΘ nemohou
b²t prvoΦφsly a tedy vÜechny zb²vajφcφ hodnoty jsou prvoΦφsly. Provedeme
to tak, ₧e v cyklu testujeme ka₧dou hodnotu a vy°azujeme ty, kterΘ jsou
d∞litelnΘ. Kdy₧ skonΦφ vn∞jÜφ cyklus, pak vÜechny hodnoty prvoΦφsel jsou
nalezeny. Program vypadß takto:
void main() {
const int sievesize = 100;
vector<int> sieve(sievesize,
1);
for (int i = 2; i * i < sievesize;
i++)
if (sieve[i])
for (int j = i + i; j < sievesize; j += i)
sieve[j] = 0;
for (int j = 2; j < sievesize;
j++)
if (sieve[j])
cout << j << " ";
cout << endl;
}
-
Datovß struktura vektor je kontejner relativn∞ pevnΘ velikosti. I kdy₧
standardnφ knihovna poskytuje slu₧by pro dynamickou zm∞nu velikosti vektoru,
tyto operace jsou nßkladnΘ a je vhodnΘ je rad∞ji nepou₧φvat. V mnoha p°φpadech
m∙₧e b²t znaΦn∞ obtφ₧nΘ urΦit p°edem velikost kolekce nebo velikost se
v pr∙b∞hu provßd∞nφ m∙₧e znaΦn∞ m∞nit. V t∞chto p°φpadech je vhodnΘ pou₧φvat
jinou datovou strukturu. Nynφ se seznßmφme s alternativnφ datovou strukturou,
kterß m∙₧e b²t pou₧ita v t∞chto situacφch, a to s datov²m typem
list.
Seznam odpovφdß intuitivnφ myÜlence dr₧enφ prvk∙ v lineßrnφ sekvenci.
NovΘ hodnoty mohou b²t p°idßvßny na nebo odstra≥ovßny ze zaΦßtku nebo konce
seznamu. Pomocφ iterßtoru lze urΦit pozici a prvky mohou b²t p°idßvßny
do nebo odstra≥ovßny z prost°edku seznamu. Ve vÜech p°φpadech vklßdacφ
nebo odstra≥ovacφ operace jsou efektivnφ a doba jejich provedenφ nenφ zßvislß
na poΦtu prvk∙ kolekce. Seznam je lineßrnφ struktura. Obsah seznamu nem∙₧e
b²t zp°φstup≥ovßn indexacφ a obecn∞ prvky mohou b²t zp°φstup≥ovßny pouze
lineßrnφm prochßzenφm vÜech hodnot.
Kdy₧ pou₧φvßme seznam, pak musφme do programu vlo₧it hlaviΦkov² soubor
list:
# include <list>
-
Jsou r∙znΘ zp∙soby deklarace seznamu. V nejjednoduÜÜφm tvaru seznam je
deklarovßn uvedenφm typu prvku seznamu. M∙₧e to b²t primitivnφ datov² typ,
typ ukazatel nebo u₧ivatelem definovan² typ. V poslednφm p°φpad∞ typ definovan²
u₧ivatelem musφ implementovat implicitnφ konstruktor a tento konstruktor
je v n∞kter²ch p°φpadech pou₧it k inicializaci nov∞ vytvo°en²ch prvk∙.
Kolekce deklarovanΘ tφmto zp∙sobem p∙vodn∞ neobsahujφ ₧ßdnΘ prvky.
list <int> list_one;
list <Widget *> list_two;
list <Widget> list_three;
Alternativnφ tvar deklarace vytvß°φ kolekci, kterß p∙vodn∞ obsahuje
n∞jak² poΦet stejn²ch prvk∙. Konstruktor pro tento tvar je deklarovßn jako
explicitnφ, co₧ znamenß, ₧e nem∙₧e b²t pou₧it jako konverznφ operßtor.
Konstruktor v tomto p°φpad∞ p°ebφrß dva parametry, velikost a poΦßteΦnφ
hodnotu. Druh² parametr je voliteln². Pokud je uveden pouze poΦet prvk∙,
pak hodnoty jsou inicializovßny implicitnφm konstruktorem, jinak jsou prvky
inicializovßny hodnotou druhΘho parametru:
list <int> list_four (5); // p∞t
prvk∙ inicializovan²ch nulou
list <double> list_five (4, 3.14);
// 4 hodnoty inicializovanΘ 3.14
list <Widget> wlist_six (4); //
implicitnφ konstruktor, 4 prvky
list <Widget> list_six (3, Widget(7));
// 3 kopie Widget(7)
Seznamy mohou b²t takΘ inicializovßny prvky z jinΘ kolekce, pomocφ
dvojice poΦßteΦnφho a koncovΘho iterßtoru. Parametry mohou b²t libovoln²m
tvarem iterßtoru a tedy kolekce m∙₧e b²t inicializovßna hodnotami z libovolnΘ
t°φdy kontejneru ze standardnφ knihovny, kterß podporuje iterßtory. Jako
alternativnφ technika m∙₧e b²t pou₧it obecn² algoritmus copy. Kdy₧
je seznam inicializovßn pomocφ copy, pak musφ b²t vytvo°en vklßdacφ
iterßtor pro p°evod v²stupnφch operacφ provßd∞n²ch operacφ copy
na vklßdßnφ. inserter vy₧aduje dva parametry; seznam do kterΘho
budou hodnoty vklßdßny a iterßtor indikujφcφ mφsto kam prvky budou vlo₧eny.
Vklßdacφ operßtory mohou b²t takΘ pou₧ity pro kopφrovßnφ prvk∙ na urΦenΘ
mφsto v existujφcφm seznamu.
list <double> list_seven (aVector.begin(),
aVector.end());
// ekvivalent p°edchozφho
list <double> list_eight;
copy (aVector.begin(), aVector.end(),
inserter(list_eight, list_eight.begin()));
Vklßdacφ iterßtory mohou b²t pou₧ity k inicializaci seznamu sekvencφ
hodnot vytvo°en²ch generßtorem. To ukazuje:
list <int> list_nine;
// inicializace seznamu 1 2 3 ... 7
generate_n (inserter(list_nine, list_nine.begin()),
7, iotaGen(1));
Kopφrovacφ konstruktor m∙₧e b²t pou₧it k inicializaci seznamu hodnotami
zφskan²mi z jinΘho seznamu. P°i°azovacφ operßtor provßdφ stejnΘ akce. V
obou p°φpadech je pou₧it pro kopφrovßnφ ka₧dΘ novΘ hodnoty p°i°azovacφ
operßtor pro typ prvku.
list <int> list_ten (list_nine);
// kopφrovacφ konstruktor
list <Widget> list_eleven;
list_eleven = list_six;
// hodnoty kopφrovßny p°i°azenφm
Metoda assign se podobß operßtoru p°i°azenφ, ale mß vφce mo₧nostφ
a v n∞kter²ch p°φpadech vy₧aduje vφce parametr∙. Jejφ pou₧φvßnφ je stejnΘ
jako u struktury vektor.
list_six.assign(list_ten.begin(), list_ten.end());
list_four.assign(3, 7);
// t°i kopie hodnoty 7
list_five.assign(12);
// 12 kopiφ hodnoty 0
Dva seznamy mohou takΘ zam∞nit svΘ obsahy operacφ swap. Nap°.
list_ten.swap(list_nine);
T°φda list obsahuje n∞kolik definic typ∙. V∞tÜinou se pou₧φvajφ
v p°φkazech deklaracφ. Nap°. iterßtor pro seznam cel²ch Φφsel m∙₧eme deklarovat
takto:
list<int>::iterator location;
Mimo iterator jsou definovßny nßsledujφcφ typy:
value_type |
Typ p°i°azen² k prvk∙m seznamu. |
const_iterator |
Iterßtor, kter² neumo₧≥uje modifikovat p°ipojenou sekvenci. |
reverse_iterator |
Iterßtor, kter² se p°esouvß sm∞rem dozadu. |
const_reverse_iterator |
Kombinace konstantnφho a reverznφho iterßtoru |
reference |
Odkaz na p°ipojen² prvek. |
const_reference |
Odkaz na p°ipojen² prvek neumo₧≥ujφcφ modifikaci prvku. |
size_type |
Typ pou₧it² k odkazovßnφ na velikost kontejneru. |
difference_type |
Typ pou₧φvan² popisu vzdßlenosti mezi iterßtory. |
allocator_type |
Typ alokßtoru pou₧it² pro veÜkerou sprßvu uklßdßnφ seznamem. |
Hodnoty lze vklßdat do seznamu r∙zn²mi zp∙soby. Prvky jsou Φasto p°idßvßny
na zaΦßtek nebo konec seznamu. Tyto ·lohy jsou provßd∞ny operacemi push_front
a push_back.
list_seven.push_front(1.2);
list_eleven.push_back (Widget(6));
Ji₧ jsme si °ekli, ₧e m∙₧eme pou₧φt vklßdacφ iterßtor spoleΦn∞ s obecn²mi
algoritmy copy nebo generate pro umφs¥ovßnφ hodnot do seznamu.
Je takΘ metoda insert, kterß nevy₧aduje vytvß°enφ vklßdacφho iterßtoru.
Hodnoty vrßcenΘ generaΦnφmi funkcemi iterßtor∙ begin a end
urΦujφ zaΦßtek a konec seznamu. Vklßdßnφ pomocφ nich je ekvivalentnφ push_front
nebo push_back. Pokud specifikujeme pouze iterßtor, pak je vlo₧en
implicitnφ prvek.
// vlo₧enφ implicitnφ hodnoty na zaΦßtek
seznamu
list_eleven.insert(list_eleven.begin());
// vlo₧enφ widget(8) na konec seznamu
list_eleven.insert(list_eleven.end(), Widget(8));
Iterßtor m∙₧e urΦovat pozici i uprost°ed seznamu. Je n∞kolik mo₧nostφ
jak vytvo°it tento iterßtor. Nap°. m∙₧eme pou₧φt v²sledek n∞jakΘ vyhledßvacφ
operace, jako je pou₧itφ obecnΘho algoritmu find. NovΘ hodnoty jsou
vklßdßny bezprost°edn∞ p°ed mφsto urΦenΘ iterßtorem. Operace insert
vracφ iterßtor urΦujφcφ mφsto vlo₧enΘ hodnoty. V²slednß hodnota (iterßtor)
m∙₧e b²t ignorovßna jak bude ukßzßno pozd∞ji.
// nalezenφ prvnφho v²skytu hodnoty 5 v seznamu
list<int>::iterator location = find(list_nine.begin(),
list_nine.end(), 5);
// a vlo₧enφ hodnoty 11 bezprost°edn∞ p°ed
nφ
location = list_nine.insert(location, 11);
Je takΘ mo₧no vklßdat pevn² poΦet kopiφ hodnoty parametru. Hodnota
vrßcenß insert nenφ pou₧ita.
line_nine.insert (location, 5, 12);
// vlo₧enφ 5 hodnot 12
Do seznamu m∙₧e b²t takΘ vlo₧ena celß sekvence urΦenß pßrem iterßtor∙.
Vrßcenß hodnota insert op∞t nenφ pou₧ita.
// vlo₧enφ celΘho obsahu jednoho seznamu
do jinΘho seznamu
list_nine.insert (location, list_ten.begin(),
list_ten.end());
Jsou r∙znΘ zp∙soby splΘtßnφ jednoho seznamu s jin²m. SplΘtßnφ se liÜφ
od vklßdßnφ v tom, ₧e prvky jsou souΦasn∞ p°idßvßny k seznamu p°φjemce
a odstra≥ovßny ze seznamu parametru. Z tohoto d∙vodu splΘtßnφ je provßd∞no
velmi efektivn∞. Metoda splice pou₧φvß iterßtor k indikaci mφsta
v p°ijφmajφcφm seznamu, kam hodnoty budou vlo₧eny. Parametrem m∙₧e b²t
cel² seznam, jeden prvek seznamu nebo podsekvence seznamu.
list_nine.splice (location, list_ten, list_ten.end());
list_nine.splice (location, list_ten);
list_ten.splice (list_ten.begin(), list_nine,
list_nine.begin(), location);
Dva se°azenΘ seznamy mohou b²t spojeny do jednoho operacφ merge.
Hodnoty ze seznamu parametru jsou spojeny do se°azenΘho seznamu a parametrov²
seznam je vyprßzdn∞n. Obecn² algoritmus stejnΘho jmΘna podporuje dva tvary.
Jeden z nich nenφ podporovßn vÜemi p°ekladaΦi.
// spojenφ s explicitnφ porovnßvacφ funkcφ
list_eleven.merge(list_six, widgetCompare);
// podobß se p°edchozφmu
list<Widget> list_twelve;
merge(list_eleven.begin(),list_eleven.end(),list_six.begin(),list_six.end(),
inserter(list_twelve, list_twelve.begin()),
widgetCompare);
list_eleven.swap(list_twelve);
Stejn∞ jako je n∞kolik r∙zn²ch zp∙sob∙ vklßdßnφ prvk∙ do seznamu, je
takΘ n∞kolik r∙zn²ch zp∙sob∙ odstra≥ovßnφ prvk∙ ze seznamu. NejΦast∞ji
pou₧φvanΘ operace k odstra≥ovßnφ hodnot jsou pop_front nebo pop_back,
kterΘ ruÜφ jeden prvek ze zaΦßtku nebo konce seznamu. Tyto metody jednoduÜe
odstranφ dan² prvek a jinak neposkytujφ ₧ßdn² u₧iteΦn² v²sledek. K prohlΘdnutφ
prvk∙ p°ed zruÜenφm pou₧ijeme metody front nebo back.
Operace erase m∙₧e b²t pou₧ita k odstran∞nφ hodnoty urΦenΘ iterßtorem.
Pro seznam, iterßtor parametru a vÜechny ostatnφ iterßtory ukazujφcφ na
stejnΘ mφsto, budou po odstran∞nφ prvku nep°φpustnΘ, ale ostatnφ iterßtory
z∙stßvajφ i nadßle v platnosti. erase m∙₧eme takΘ pou₧φt k odstran∞nφ
celΘ podsekvence urΦenΘ pßrem iterßtor∙.
list_nine.erase (location);
// zruÜenφ hodnot mezi prvnφm v²skytem 5
a nßsledujφcφ v²skytem 7
list<int>::iterator location = find(list_nine.begin(),
list_nine.end(), 5);
list<int>::iterator location2 = find(location,
list_nine.end(), 7);
list_nine.erase (location, location2);
Metoda remove odstra≥uje vÜechny v²skyty danΘ hodnoty ze seznamu.
remove_if
odstra≥uje vÜechny prvky spl≥ujφcφ danou podmφnku. Alternativou k jejich
pou₧itφ jsou obecnΘ algoritmy stejnΘho jmΘna. Tyto obecnΘ algoritmy nezmenÜujφ
velikost seznamu, pouze p°esouvajφ zb²vajφcφ prvky na zaΦßtek seznamu a
vracejφ iterßtor urΦujφcφ prvnφ nep°esunut² prvek. Tato hodnota m∙₧e b²t
pou₧ita metodou erase k odstran∞nφ zb²vajφcφch hodnot.
list_nine.remove(4);
// odstra≥uje vÜechny 4
list_nine.remove_if(divisibleByThree);
// odstra≥uje vÜe d∞litelnΘ 3
// ekvivalent p°edchozφho
list<int>::iterator location3=remove_if(list_nine.begin(),list_nine.end(),
divisibleByThree);
list_nine.erase(location3, list_nine.end());
Operace unique ruÜφ vÜe mimo prvnφch prvk∙ z ka₧dΘ ekvivalentnφ
skupiny prvk∙ v seznamu. Seznam nemusφ b²t se°azen. Alternativnφ verze
p°ebφrß binßrnφ funkci, porovnßvß sousednφ prvky pomocφ tΘto funkce a odstra≥uje
druhΘ hodnoty v t∞ch situacφch, kdy funkce vracφ hodnotu true. V
nßsledujφcφm p°φklad∞ binßrnφ funkce je operßtor v∞tÜφ ne₧, kter² odstranφ
vÜechny prvky menÜφ ne₧ p°edchozφ prvek.
list_nine.unique();
// explicitn∞ danß porovnßvacφ funkce
list_nine.unique(greater<int>());
// stejnΘ jako p°edchozφ
location3 = unique(list_nine.begin(), list_nine.end(),
greater<int>());
list_nine.erase(location3, list_nine.end());
Metoda size vracφ poΦet prvk∙ dr₧en²ch v kontejneru. Funkce
empty
vracφ true pokud kontejner je prßzdn² a je efektivn∞jÜφ ne₧ porovnßvßnφ
zφskanΘ velikosti s nulou.
cout << "PoΦet prvk∙: " << list_nine.size
() << endl;
if ( list_nine.empty () ) cout << "seznam
je prßzdn² " << endl;
else cout << "seznam nenφ prßzdn² "
<< endl;
Metoda resize m∞nφ velikost seznamu na hodnotu specifikovanou
parametrem. Z konce seznamu jsou odebrßny nebo p°idßny hodnoty podle pot°eby.
Voliteln² druh² parametr m∙₧e b²t pou₧it jako poΦßteΦnφ hodnota pro p°idßvanΘ
prvky.
// zφskß velikost 12, p°idßvanΘ prvky jsou
17 (v p°φpad∞ pot°eby)
list_nine.resize (12, 17);
Metody front a back vracejφ, ale neodstra≥ujφ prvnφ a
poslednφ prvek kontejneru. Pro seznam, p°φstup k ostatnφm prvk∙m je mo₧n²
pouze odstra≥ovßnφm prvk∙ (dokud po₧adovan² prvek se nestane prvnφm nebo
poslednφm) nebo pomocφ pou₧itφ iterßtor∙.
Jsou r∙znΘ typy iterßtor∙, kterΘ mohou b²t vytvo°eny pro seznamy. Funkce
begin
a end vytvß°ejφ iterßtory kterΘ prochßzejφ seznamy dop°edu. Pro
seznamy to jsou obousm∞rnΘ iterßtory. Alternativnφ funkce
rbegin
a rend vytvß°ejφ reverznφ iterßtory.
Seznamy neposkytujφ p°φmo ₧ßdnou metodu, kterß m∙₧e b²t pou₧ita k urΦenφ,
zda specifikovanß hodnota je obsa₧ena v kolekci. Pro tento ·Φel ale m∙₧eme
pou₧φt obecnΘ algoritmy find nebo count. Nßsledujφcφ p°φkazy
nap°. testujφ zda seznam cel²ch Φφsel obsahuje prvek 17.
int num = 0;
count(list_five.begin(), list_five.end(),
17, num);
if (num > 0) cout << "obsahuje 17"
<< endl;
else cout << "neobsahuje 17" <<
endl;
// nebo
if (find(list_five.begin(), list_five.end(),
17) != list_five.end())
cout << "obsahuje 17"
<< endl;
else cout << "neobsahuje 17" <<
endl;
Metoda sort umφs¥uje prvky do vzestupnΘho po°adφ. Pokud je po₧adovßn
jin² operßtor ne₧ <, pak musφ b²t p°edßn jako parametr.
list_ten.sort ( );
// se°azenφ prvk∙
list_twelve.sort (widgetCompare);
// °azenφ s porovnßvacφ funkcφ
Na seznamy m∙₧eme aplikovat °adu vyhledßvacφch funkcφ. Jednß se o find,
find_if,
mismatch,
max_element,
min_element,
search
apod. Ve vÜech p°φpadech v²sledkem je iterßtor, kter² m∙₧e b²t dereferencovßn
k zφskßnφ urΦenΘho prvku nebo pou₧it jako parametr v nßsledujφcφ operaci.
Na seznam mohou b²t takΘ aplikovßny operace, kterΘ m∞nφ jeho pozice.
N∞kterΘ z nich jsou poskytnutΘ jako metody. Nap°. metoda reverse
m∞nφ po°adφ prvk∙ v seznamu.
list_ten.reverse();
Obecn² algoritmus transform m∙₧e b²t pou₧it k modifikaci ka₧dΘ
hodnoty v kontejneru, kde jako vstup a v²stup se pou₧φvß stejn² kontejner.
Nßsleduje nap°. zv∞tÜenφ ka₧dΘho prvku v kontejneru o 1. K vytvo°enφ nutnΘ
unßrnφ funkce, prvnφ parametr binßrnφ celoΦφselnΘ funkce je sestaven v
jednu hodnotu. Verze transform, kterß manipuluje na dvou paralelnφch
sekvencφch m∙₧e b²t pou₧ita podobn²m zp∙sobem.
transform(list_ten.begin(), list_ten.end(),
list_ten.begin(), bind1st(plus<int>(), 1));
Podobn∞ funkce replace a replace_if mohou b²t pou₧ity
k nahrazovßnφ prvk∙ v seznamu specifikovanou hodnotou. Se seznamy m∙₧eme
takΘ provßd∞t rotace a rozd∞lovßnφ.
// nalezenφ hodnoty 5 a rotace okolo nφ
location = find(list_ten.begin(), list_ten.end(),
5);
rotate(list_ten.begin(), location, list_ten.end());
// Φßst pou₧φvß hodnoty v∞tÜφ ne₧ 7
partition(list_ten.begin(), list_ten.end(),
bind2nd(greater<int>(), 7));
Funkce next_permutation a prev_permutation mohou b²t
pou₧ity ke generovßnφ nßsledujφcφ (nebo p°edchozφ) permutace kolekce hodnot.
next_permutation (list_ten.begin(), list_ten.end());
Algoritmus for_each aplikuje funkci na ka₧d² prvek kolekce.
Algoritmus accumulate redukuje kolekci na skalßrnφ hodnotu. M∙₧e
to b²t nap°. pou₧ito na v²poΦet souΦtu seznamu.
cout << "SouΦet seznamu je: " <<
accumulate(list_ten.begin(), list_ten.end(), 0) << endl;
Dva seznamy mohou b²t porovnßvßny jeden s druh²m. Jsou si rovny, pokud
majφ stejnou velikost a vÜechny odpovφdajφcφ prvky si jsou rovny. Seznam
je menÜφ ne₧ jin² seznam, pokud je lexikograficky menÜφ.
-
Pro ilustraci pou₧itφ n∞kter²ch operacφ se seznamem pou₧ijeme p°φklad jednoduchΘho
systΘmu sprßvy zßsob. P°edpoklßdejme podnik nazvan² WorldWideWidgetWorks,
vy₧adujφcφ programov² systΘm pro sprßvu sv²ch zßsob za°φzenφ (widget).
Za°φzenφ jsou rozliÜovßny identifikaΦnφmi Φφsly:
class Widget {
public:
Widget(int a = 0) : id(a) {
}
void operator = (const Widget&
rhs) { id = rhs.id; }
int id;
friend ostream & operator
<< (ostream & out,const Widget & w)
{ return out
<< "Widget " << w.id; }
friend bool operator == (const
Widget& lhs, const Widget& rhs)
{ return lhs.id
== rhs.id; }
friend bool operator< (const
Widget& lhs, const Widget& rhs)
{ return lhs.id
< rhs.id; }
};
Stav zßsob je reprezentovßn dv∞ma seznamy. Jeden seznam reprezentuje
stav za°φzenφ na sklad∞ zatφmco druh² seznam typy za°φzenφ ji₧ objednanΘ
zßkaznφky. Prvnφ je seznam za°φzenφ, zatφmco druh² je seznamem identifikaΦnφch
typ∙ za°φzenφ. Pro zpracovßnφ naÜich zßsob mßme dva p°φkazy: prvnφ order
zpracovßvß objednßvky, zatφmco druh² receive zpracovßvß prodeje
nov²ch za°φzenφ.
class inventory {
public:
void order (int wid);
// zpracovßnφ objednßvky pro za°φzenφ typu wid
void receive (int wid);
// p°φjem za°φzenφ typu wid v dodßvce
private:
list<Widget> on_hand;
list<int> on_order;
};
Kdy₧ p°ijmeme novou dodßvku za°φzenφ, pak porovnßme identifikaΦnφ Φφsla
za°φzenφ se seznamem ji₧ objednan²ch typ∙ za°φzenφ. Pro hledßnφ v ji₧ p°ijat²ch
objednßvkßch pou₧ijeme find a bezprost°edn∞ prodßme objednanΘ za°φzenφ.
Jinak za°φzanφ p°edßme na sklad.
void inventory::receive (int wid)
{
cout << "Received shipment
of widget type " << wid << endl;
list<int>::iterator weneed
=
find (on_order.begin(),
on_order.end(), wid);
if (weneed != on_order.end())
{
cout <<
"Ship " << Widget(wid)
<< " to fill back order" << endl;
on_order.erase(weneed);
}
else
on_hand.push_front(Widget(wid));
}
Kdy₧ zßkaznφk objednßvß novΘ za°φzenφ, prohledßme seznam za°φzenφ na
sklad∞ k urΦenφ zda objednßvku m∙₧eme vy°φdit okam₧it∞. K prohledßnφ seznamu
pou₧ijeme funkci find_if. Pot°ebujeme k tomu binßrnφ funkci, kterß
p°ebφrß jako sv∙j parametr za°φzenφ a urΦuje, zda za°φzenφ odpovφdß po₧adovanΘmu
typu. M∙₧eme to provΘst p°edßnφm obecnΘ binßrnφ testovacφ funkce za°φzenφ
a spojit druh² parametr na specifick² typ za°φzenφ. K pou₧itφ funkce bind2nd
je zapot°ebφ, aby binßrnφ funkce byla instance t°φdy binary_function.
NaÜi obecnou testovacφ funkci zapφÜeme takto:
class WidgetTester : public binary_function<Widget,
int, bool> {
public:
bool operator () (const Widget
& wid, int testid) const
{ return wid.id
== testid; }
};
Funkce order je pak zapsßna takto:
void inventory::order (int wid)
{
cout << "Received order
for widget type " << wid << endl;
list<Widget>::iterator wehave
=
find_if (on_hand.begin(), on_hand.end(),
bind2nd(WidgetTester(), wid));
if (wehave != on_hand.end())
{
cout <<
"Ship " << *wehave << endl;
on_hand.erase(wehave);
}
else
{
cout <<
"Back order widget of type " << wid << endl;
on_order.push_front(wid);
}
}
-
STL je mo₧no pou₧φvat i v C++ Builderu. Jako p°φklad pou₧itφ STL si ukß₧eme
demonstraΦnφ aplikaci, kterß ukazuje °adu mo₧nostφ STL. Jednß se ji₧ op∞t
o hotovou aplikaci. Stßhn∞te si ji a vyzkouÜejte.
V aplikaci m∙₧ete zadat, co chcete vyzkouÜet a v²sledky jsou zobrazeny
v komponent∞ Memo. Zajφmav∞jÜφ je ale podφvat se jak tyto ukßzky
jsou naprogramovanΘ a sna₧it se pochopit, jak s touto knihovnou pracovat.
Mezi soubory, kterΘ jste si stßhli jsou i p°φklady pou₧itΘ v p°edchozφm
textu. SystΘm sprßvy inventß°e je v souboru widwork.cpp a program
Eratosova sφta v souboru sieve.cpp. S dalÜφmi typy kontejner∙ se
seznßmφme v nßsledujφcφ kapitole.