Skip to content

Commit 136a5c7

Browse files
committed
added vektor
1 parent d72a4a7 commit 136a5c7

1 file changed

Lines changed: 212 additions & 0 deletions

File tree

containers/vector.md

Lines changed: 212 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,212 @@
1+
# Vector
2+
3+
Vector je kontainer, ktorý je súčasťou štandardnej knižnice C++ a poskytuje intedrface dynamického pola. To znamená, že `std::vector` nám umožňuje ukladať a spravovať dáta vo forme poľa, pričom veľkosť tohto poľa môžete meniť počas behu programu. Vector patrí k základným a najpoužívanejším dátový štruktúram vôbec.
4+
5+
`std::vector` sídli v hlavičkovom súbore `#include <vector>`.
6+
7+
*Funkcie, ktoré sa ďalej budú spomínať majú veľa overloadov, všetky neuvádzame, dajú sa ľahko nájsť v dokumentácií.*
8+
9+
Niektoré dôležité vlastnosti a operácie, ktoré sú spojené s `std::vector`:
10+
11+
1. **Dynamická veľkosť**: Hlavnou výhodou `std::vector` je schopnosť meniť svoju veľkosť počas behu programu. Môžeme pridávať a odoberať prvky podľa potreby, čo je veľmi užitočné, ak nevieme presne, koľko prvkov budeme potrebovať vopred, ako to potrebuje pri statických poliach (e.g. `int[]`).
12+
13+
2. **Rýchly prístup** Prístup k prvkom `std::vector` je rýchly, pretože vnútorné dáta sú usporiadané v pamäti ako kontinuálny blok, čo umožňuje použiť smerníkovú aritmetiku aby sme sa rýchlo dostali k prvku, ktorý nás zaujíma. Pre prístup k prvku môžeme použiť operátor `[]` rovnako ako pri bežných poľových dátových štruktúrach.
14+
15+
3. **Automatická správa pamäte**: `std::vector` spravuje svoju pamäť automaticky. To znamená, že sa stará o alokáciu a dealokáciu pamäte, čo nás zbavuje starostí s ručnou správou pamäte.
16+
17+
4. **Iterátory**: Môžeme použiť interface iterátorov na prechádzanie a manipuláciu s prvkami v `std::vector`.
18+
19+
## Vytváranie
20+
21+
### Vytvorenie prázdneho vektora
22+
23+
Ak chcete vytvoriť prázdny vektor a neskôr doň pridávať prvky, jednoducho vytvorte vektor bez uvedenia počiatočných hodnôt.
24+
25+
```cpp
26+
std::vector<int> numbers;
27+
```
28+
29+
### Inicializácia vektora s určenou veľkostou
30+
31+
Môžeme inicializovať vektor s určitým počtom prvkov, ktoré majú počiatočnú hodnotu. Nato použijeme konštruktor s dvoma argumentami - počtom prvkov a ich počiatočnou hodnotou. *Pozor toto nie je iba predalokovanie miesta.*
32+
33+
```cpp
34+
std::vector<int> numbers(5, 1); // Vektor s 5 jednotkami
35+
```
36+
37+
### Inicializácia vektora z existujúceho poľa pomocou iterátorov
38+
39+
Môžeme inicializovať vektor na základe existujúceho poľa alebo iného vektora. Na to môžete použiť konštruktor `std::vector` s dvoma iterátormi ukazujúcimi na začiatok a koniec rozsahu, ktorý chceme skopírovať.
40+
41+
```cpp
42+
int array[] = {1, 2, 3, 4, 5};
43+
std::vector<int> numbers(array + 1, array + 5); // 2, 3, 4, 5
44+
45+
std::vector<int> numbers2(numbers.begin(), numbers.end() - 1); // 2, 3, 4
46+
```
47+
48+
### Inicializácia vektora pomocou zoznamu inicializácie
49+
50+
Od C++11 môžeme vytvoriť a inicializovať vektor pomocou zoznamu inicializácie *(angl. initializer list)*.
51+
52+
```cpp
53+
std::vector<int> numbers = {1, 2, 3, 4, 5};
54+
```
55+
56+
## Prechádzanie cez vektor
57+
58+
Na iteráciu vektora používame funkcie `begin()` a `end()`. Tieto funkcie poskytujú iterátory na začiatok a koniec kontajnera, čo umožňuje prechádzať jeho prvky pomocou klasických `for`-cyklusov. Alebo môžeme využiť automatické rozsahé for-cyklusov (*range-based for loops*). Oba spôsoby sú OK, akurát druhý je modernejší.
59+
60+
### Pomocou `begin()` a `end()` a klasického `for`-cyklu
61+
62+
```cpp
63+
std::vector<int> numbers = {1, 2, 3, 4, 5};
64+
65+
for (std::vector<int>::iterator it = numbers.begin(); it != numbers.end(); ++it) {
66+
std::cout << *it << " "; // výpis: 1 2 3 4 5
67+
}
68+
```
69+
70+
### Pomocou rozsahového for-cyklu
71+
72+
Rozsahový for-cyklus umožňuje jednoduchšiu iteráciu vektora bez priameho použitia iterátorov. Aj keď pod kapotou sa používajú iterátory a teda oba spôsoby iterácie sú viacmenej ekvivalentné.
73+
74+
```cpp
75+
std::vector<int> numbers = {1, 2, 3, 4, 5};
76+
77+
for (int num : numbers) {
78+
std::cout << num << " "; // výpis: 1 2 3 4 5
79+
}
80+
```
81+
82+
## Pridávanie prvkov
83+
84+
### Na koniec
85+
86+
Pridávanie na koniec `std::vector` sa robí pomocou funkcie `push_back`. Operácia je veľmi rýchla O(1) v priemernom prípadne. Ak sa však veľkosť vektora približuje svojmu maximálnemu kapacitnému limitu, vektor môže vyžadovať alokáciu nového bloku pamäte a kopírovanie existujúcich prvkov do nového bloku. Toto môže mať lineárnu časovú zložitosť O(n), kde n je aktuálna veľkosť vektora.
87+
88+
Vektor si teda zvyčajne drží väčší blok pamäte ako reálne potrebuje. Preto väčšinou má miesto na ešte jeden prvok. Ak ale zrovna nemá, tak je pridanie drahé. Stratégie ako to vektor robí sa líša od vendora štandardnej knižnice. Zvyčajne sa veľkosť vektora zdvojnásobuje.
89+
90+
Povedzme, že máme vektor s 8 prvkami. Pridanie ďalšieho na koniec. Urobí realokáciu a 8 rvkov sa musí posununúť. Lenže teraz má vektor dvojnásobnú kapacitu, teda 16 prvkov, preto ďalších 7 pridaní na koniec nebude vyžadovať realokáciu.
91+
92+
### Na začiatok a inde do vektora
93+
94+
Funkcia `insert` má dva parametre. Prvý je pozícia, kam chceme vložiť, druhý je čo chceme vložiť.
95+
96+
```cpp
97+
std::vector<int> numbers = {1, 2, 3, 4, 5};
98+
numbers.insert(numbers.begin() + 2, 99); // 1, 2, 99, 3, 4, 5
99+
```
100+
101+
Ako vidíme, pozícia je určená iterátorom a nie indexom. To ale nie je problém, lebo stačí urobiť `vec.begin() + idx` a konvertujeme index `idx` na iterátor do vektora `vec`.
102+
103+
## Veľkosť a kapacita
104+
105+
V kontexte štandardného vektora v jazyku C++ je dôležité rozlišovať medzi veľkosťou a kapacitou vektora. Obe tieto vlastnosti sú dôležité pre správu dát vektora. Ich poznaním sa vieme vyhnúť nedefinovanému správaniu.
106+
107+
### Veľkosť (size)
108+
109+
Veľkosť vektora označuje počet prvkov, ktoré momentálne obsahuje. Na zistenie veľkosti vektora používate funkciu `size()`.
110+
111+
```cpp
112+
std::vector<int> numbers = {1, 2, 3, 4, 5};
113+
size_t size = numbers.size(); // size obsahuje hodnotu 5
114+
```
115+
116+
Veľkosť vektora sa automaticky mení, keď pridávate alebo odoberáte prvky. Ak ju chceme zmeniť explicitne, tak môžeme pomocou `resize(new_size)`, ak je nová veľkošt väčšia, tak sa nové prvky default inicializujú.
117+
118+
### Kapacita (capacity):
119+
120+
Kapacita vektora označuje maximálny počet prvkov, ktoré môže vektor obsahovať, bez potreby reálnej alokácie ďalšej pamäti. Kapacitu môžete zistiť pomocou funkcie `capacity()`. Kapacita sa môže zvýšiť počas operácie `push_back()` alebo iných operácií, ktoré pridávajú prvky do vektora. Štandardne sa kapacita nezmenšuje pri odoberaní prvkov.
121+
122+
Keď je veľkosť vektora rovnaká ako kapacita, znamená to, že vektor je plný a ďalšie pridávanie prvkov môže viesť k reálnej alokácií pamäte, čo môže byť náročné na výkon. Preto v kritickych miestach je vhodné poznať kapacitu a podľa potreby ju meniť, aby sa minimalizovala realokácia pamäte.
123+
124+
Ak chcete explicitne nastaviť kapacitu vektora, môžete použiť metódu `reserve()`. Znova vieme iba zväčšovať. Ak chceme kapacitu zmenšiť máme funkciu `shrink_to_fit()`, ktorá **by mala** presunuť vektor do bloku pamäte, ktorý presne zodpovedá jeho aktuálnym potrebám.
125+
126+
```cpp
127+
std::vector<int> numbers;
128+
129+
// explicitné rezervovanie kapacity pre 10 prvkov
130+
numbers.reserve(10);
131+
132+
// teraz môžeme pridávať prvky
133+
for (int i = 1; i <= 10; i++) {
134+
numbers.push_back(i); // nikdy neurobi realokaciu
135+
}
136+
```
137+
138+
## Najčastejšie chyby
139+
140+
Vektor is síce extrémne rýchly, ale táto rýchlost je dosiahnutá velkým množstvom nedefinovaného správania.
141+
142+
### Operátor []
143+
144+
Operátor prístupu k prvkom nekontroluje či pristupujeme správne, takže sa nám môže stať, že čitame neinicializovanú pamäť, alebo ešte horšie spôsobime porušenie ochrany pamäte.
145+
146+
```cpp
147+
std::vector<int> a = { 1, 2, 3, 4, 5};
148+
149+
std::cout << a[5]; // garbage
150+
151+
a[10000] = 0; // access violation or segmentation fault
152+
```
153+
154+
### Neplatné iterátory
155+
156+
Iterátory sa zneplatia, keď prvok na ktorý ukazujú sa z vektora odstráni. Rovnako sú zneplatnené aj iterátory za prvkom, ktorý mažeme. To je ešte celkom pochopiteľné, navyše ale iterátory prestávajú byť platné aj keď vektor urobí realokáciu.
157+
158+
```cpp
159+
std::vector<int> a = { 1, 2, 3, 4, 5};
160+
161+
auto it = a.begin() + 1; // points to 2
162+
163+
a.erase(a.end() - 1);
164+
// it still valid
165+
166+
a.erase(a.begin());
167+
// it invalid
168+
```
169+
170+
Pri `erase` ešte pozor `vec.erase(vec.end())` je nedefinované správanie.
171+
172+
```cpp
173+
std::vector<int> a = { 1, 2, 3, 4, 5};
174+
175+
auto it = a.begin() + 1; // points to 2
176+
177+
a.push_back(0);
178+
// it may be invalid
179+
```
180+
181+
V predchádzajúcom príklade nie je úplne jasné, či je iterátor platný, alebo nie. Môžeme to zistiť, tak že skontrolujeme kapacitu.
182+
183+
```cpp
184+
std::vector<int> a = { 1, 2, 3, 4, 5};
185+
186+
auto it = a.begin() + 1; // points to 2
187+
188+
auto space_available = a.capacity() > a.size();
189+
a.push_back(0);
190+
191+
if (space_available) {
192+
// it is valid
193+
} else {
194+
// it is invalid
195+
}
196+
```
197+
198+
### Modifikácia vektora počas iterácie
199+
200+
```cpp
201+
std::vector<int> a = { 1, 2, 3, 4, 5};
202+
203+
for (auto i : a) {
204+
if (i % 2 == 1) {
205+
a.push_back(0);
206+
}
207+
}
208+
```
209+
210+
Mohli by sme sa domievať, že kód vyššie na konci nechá vektor naplnený hodnotami `1, 2, 3, 4, 5, 0, 0, 0` (jedna nula za každé nepárne číslo). Nie je tomu ale tak, lebo range based for cyklus používa iterátory a `push_back` ich môže zneplatniť. Preto kód vyššie môźe viesť k nedefinovanému správaniu.
211+
212+
Vo všeobecnosti nie je dobrý nápad iterovať vektor a meniť počet prvkov. Niektoré použitia sú ale OK. Napríklad `erase` vo for cykle. Stačí si zapamätať, čo zneplatnuje iterátory a mali by ste byť OK.

0 commit comments

Comments
 (0)