std::array<T,dim>
std::array<T, dim>
je polje elemenata tipa T
i dimenzije dim
.
Dimenzija mora biti konstanta poznata za vrijeme kompilacije.
Tip je definiran u zaglavlju <array>
.
Spremnik std::array
ne dozvoljava promjenu broja elemenata. Njegovi se elementi
(ako je moguće) alociraju na stogu, a ne u dinamičkoj memoriji (heap-u). Garantirano je da se
elementi nalaze na konsekutivnim lokacijama.
Elementima u polju pristupamo ili pomoću indeksa ili pomoću iteratora.
Primjer.
#include <array>
std::array<int, 4> a{2,4,6,1};
std::array<int, 6> b; // vrijednost elemenata nedefinirana
b = a; // sve elemente iz a pridruži elementima iz b
for(auto x : a) std::cout << x << ",";
std::cout << std::endl;
std::array<double, 4> b{3.14, 2.71}; // zadnja dva elementa
//inicijalizirana su nulom.
// Koristima reverzne iteratore.
for(auto it=b.rbegin(); it != b.rend(); ++it)
std::cout << *it <<",";
std::cout << std::endl;
std::cout << b[0] << std::endl; // pristup preko indeksa
std::cout << b.at(4) << std::endl; // pristup preko indeksa s provjerom
// indeksa -- izbacuje izuzetak
Operacije
Operacija | Značenje |
---|---|
|
Defaultni konstruktor |
|
Konstruktor kopije |
|
Konstruktor kopije |
|
Konstruktor kopije premještanjem |
|
Konstruktor kopije premještanjem |
|
Konstrukcija iz inicijalizacijske liste |
Operacija | Značenje |
---|---|
|
Jednakost |
|
Različitost |
Operacija | Značenje |
---|---|
|
Pridruži sve elemente iz |
|
Pridruživane premještanjem |
|
Postavi sve elemente na |
|
Podatke iz |
Operacija | Značenje |
---|---|
|
Vraća element s indeksom |
|
Vraća element s indeksom |
|
Vraća prvi element |
|
Vraća zadnji element |
Operacija | Značenje |
---|---|
|
Vraća iterator na poziciju prvog elementa |
|
Vraća iterator na poziciju iza zadnjeg elementa |
|
Vraća konstantan iterator na poziciju prvog elementa |
|
Vraća konstantan iterator na poziciju iza zadnjeg elementa |
|
Vraća reverzni iterator na poziciju prvog elementa reverznog niza |
|
Vraća reverzni iterator na poziciju iza zadnjeg elementa reverznog niza |
|
Vraća konstantan reverzni iterator na poziciju prvog elementa reverznog niza |
|
Vraća konstantan reverzni iterator na poziciju iza zadnjeg elementa reverznog niza |
std::vector<T>
- konstrukcija
std::vector<T>
je dinamički alocirano polje tipa T
. vector
se brine o dinamičkoj alokaciji
i dealokaciji memorije.
Elementi u vektoru se nalaze u konsekutivnim lokacijama, što je važno radi efikasnosti pristupa.
Da bismo mogli koristiti vector
treba uključiti njegovu datoteku zaglavlja:
#include <vector>
Konstruktor koji ne uzima argumente konstruira prazan vektor.
std::vector<std::string> svec; // prazan vector stringova
Konstruktor može uzeti broj elemenata i inicijalizacijsku vrijednost.
vector<double> a; // prazan vektor. Ne sadrži niti jedan element
vector<double> b(10); // vektor od 10 double-ova, inicijaliziranih nulom
vector<std::string> e(16) // vektor od 16 praznih stringova
vector<double> c(128,0.77); // Vektor od 128 double-ova,
// inicijaliziranih sa 0.77
vector<double> d{1.1,1.2,1.3,1.4}; // inicijalizacija popisom elemenata
Za inicijalizaciju možemo koristiti i raspon elemenata iz standardnog polja elemenata:
char niz[] = {'a','b','c'};
vector<char> vniz(niz,niz+3); // {'a','b','c'}
Ako želimo elemente incijalizirati elementima spremnika drugog tipa trebamo koristiti iteratore:
std::list<int> li{1,2,3,4};
std::vector<double> vd(li.begin(), li.end()); // {1,2,3,4}
Operacija | Značenje |
---|---|
|
Dodijeljeni konstruktor; kreira prazan vektor |
|
Konstruktor kopije |
|
Konstruktor kopije |
|
Konstruktor kopije premještanjem |
|
Konstruktor kopije premještanjem |
|
Kreira vektor od |
|
Kreira vektor od |
|
Kreira vektor inicijaliziran elementima iz raspona |
|
Kreira vektor inicijaliziran elementima iz inicijalizacijske liste |
|
Kreira vektor inicijaliziran elementima iz inicijalizacijske liste |
|
Destruktor |
std::vector<T>
- size
i capacity
Vektor raste automatski kada nema više
mjesta za novi element u vektoru. Metoda size()
daje broj elemenata u vektoru, a metoda capacity()
daje broj elemenata koji vektor može primiti bez realokacije.
std::vector<int> a;
std::cout << a.size() << std::endl; // ispisuje nulu
std::cout << a.capacity() << std::endl; // ispisuje nulu
assert(a.empty()); // a je prazan
std::vector<int> b(6);
std::cout << b.size() << std::endl; // ispisuje 6
std::cout << b.capacity() << std::endl; // ispisuje 6
Metoda empty()
vraća istinu ako je vektor prazan, odnosno laž ako nije. Makro
assert()
je definiran u zaglavlju <cassert>
.
Metodom push_back()
ubacujemo novi element iz zadnjeg. Ako nema dovoljno mjesta za novi element
vektor se realocira: osigurava se dovoljno mjesta drugdje u memoriji, kopiraju se svi stari element i dodaje se novi.
// Ubaci na kraj vektora novi element = 11
b.push_back(11);
std::cout << b.size() << std::endl; // ispisuje 7
std::cout << b.capacity() << std::endl; // ispisuje 12
Pri realokaciji veličina vektora se približno podvostručuje.
Kapacitet vektora se može povećati i pomoću reserve()
metode koja osigurava da vektor ima
dovoljno prostora (ali ne mijenja broj elemenata u njemu).
b.reserve(16);
std::cout << b.size() << std::endl; // ispisuje 7
std::cout << b.capacity() << std::endl; // ispisuje 16
Broj elemenata se može promijeniti pomoću metode resize(n)
. Ako se povećava broj elemenata, novi
elementi će biti inicijalizirani nulom, odnosno bit će pozvan konstruktor koji ne uzima argumente.
Ako se broj elementa smanjuje elementi koji ostaju u spremniku čuvaju svoju vrijednost. Kapacitet
vektora najčešće ostaje isti.
Napomena
|
Treba razlikovati resize() i reserve() . Ako na praznom vektoru v pozovemo v.reserve(100)
on ostaje prazan, ali ima rezerviranu memoriju za 100 elemenata. S druge strane, ako na praznom vektoru v pozovemo v.resize(100)
on postaje vektor od 100 elementa inicijaliziranih dodijeljenim konstruktorom. |
Operacija | Značenje |
---|---|
|
Vraća |
|
Vraća broj elemenata u vektoru |
|
Vraća maksimalni mogući broj elemenata u vektoru |
|
Vraća maksimalni mogući broj elemenata u vektoru bez realokacije |
|
Povećava kapacitet na |
|
Promijeni broj elemenata spremnika na |
|
Promijeni broj elemenata spremnika na |
|
Reducira kapacitet na broj elemenata u vektoru |
|
Vraća |
|
Vraća |
std::vector<T>
- operacije na čitavom spremniku
Ako spremniku pridružujemo spremnik istog tipa za to možemo koristiti operator pridruživanja. Ako vektor na lijevoj strani nije prazan njegovi elementi će biti uništeni i zamijenjeni elementima vektora desne strane.
std::vector<double> b{1.0,2.0,3.0}
std::vector<double> d{4.0,3.0,2.0,1.0,0.0}; // 5 elemnata
d = b;
std::cout << "Size = " << b.size(); // ispisuje 3
Kada želimo vektoru pridružiti elemente iz spremnika drugog tipa trebamo koristiti metodu assign()
.
std::array<double, 3> ar{1.0,2.0,3.0};
std::vector<double> b; // prazan vektor
b.assign(5, 1.0); // sada ima 5 elementa jednakih 1.0
b.assign(ar.begin(),ar.end()); // Sada ima 3 elementa: 1.0,2.0 i 3.0
std::cout << "Size = " << b.size(); // ispisuje 3
U svim ovim operacijama ako spremnik na lijevoj strani nije prazan njegovi elementi se uništavaju.
Operacija | Značenje |
---|---|
|
Pridruži sve elemente spremika |
|
Pridruži premještanjem sve elemente spremika |
|
Pridruži sve elemente inicijalizacijske liste |
|
Pridruži |
|
Pridruži elemente iz raspona |
|
Pridruži sve elemente inicijalizacijske liste |
|
Zamjeni sadržaj spremnika |
|
Zamjeni sadržaj spremnika |
Napomena
|
C++ smatra iste spremnike s različitim tipom elemenata različitim tipovima. Tako su
std::vector<double> i std::vector<float> različiti tipovi i između njih ne možemo koristiti
operator pridruživanja. |
std::vector<T>
- dohvat elemenata
Dohvat elemenata ide kroz cjelobrojni indeks ili iterator.
Elemente vektora možemo dohvatiti pomoću uglatih zagrada i cjelobrojnih indeksa kao i kod običnog polja.
for(unsigned int i=0; i != b.size(); ++i) b[i] = std::log(2*i+1);
std::cout << "b[1] = "<< b[1] << std::endl; // nema provjere indeksa
double xx = b.at(14); // at() umjesto []: dohvat pomoću indeksa s
// provjerom granica - izbacuje izuzetak
Indeks počinje od nule; metoda size()
iz klase vector<T>
nam daje broj elemenata u polju.
Iteratori služe za kretanje kroz spremnike i ponašaju se kao pokazivači. Svaki spremnik ima metodu
begin()
koja vraća iterator koji pokazuje na prvi element spremnika i metodu end()
koja vraća
iterator koji pokazuje na jedno mjesto iz zadnjeg u spremiku.
for(auto it = b.begin(); it != b.end(); ++it)
std::cout << *it << std::endl; // dohvat pomoću iteratora
assert(*b.begin() == b[0]);
Moguće je direktno dobiti referencu na prvi odnosno zadnji element vektora kroz
metode front()
i back()
:
b.front() += 1;
b.back() = 7;
Ne provjerava se da li element postoji.
Metode data()
vraća pokazivač na prvi elemet u spremniku, dakle pokazivač na memoriju
koja sadrži sve elemente. To omogućava da vector
koristimo kao C-polje.
int * pb = b.data();
assert(pb == &b[0]);
Operacija | Značenje |
---|---|
|
Vraća element s indeksom |
|
Vraća element s indeksom |
|
Vraća prvi element |
|
Vraća zadnji element |
|
Vraća pokazivač na prvi element vektora. |
Operacija | Značenje |
---|---|
|
Vraća iterator na poziciju prvog elementa |
|
Vraća iterator na poziciju iza zadnjeg elementa |
|
Vraća konstantan iterator na poziciju prvog elementa |
|
Vraća konstantan iterator na poziciju iza zadnjeg elementa |
|
Vraća reverzni iterator na poziciju prvog elementa reverznog niza |
|
Vraća reverzni iterator na poziciju iza zadnjeg elementa reverznog niza |
|
Vraća konstantan reverzni iterator na poziciju prvog elementa reverznog niza |
|
Vraća konstantan reverzni iterator na poziciju iza zadnjeg elementa reverznog niza |
std::vector<T>
- ubacivanje elementa
Kod vektora ubacivanje novog elementa je efikasno samo na kraju vektora. Za to koristimo metodu
push_back()
. Zadnji se element može efikasno izbaciti iz vektor metodom pop_back()
.
Kada veličina vektora dosegne njegov kapacitet, pri ubacivanju novog elementa dolazi do realokacije vektora. Rezervira se nova memorijska lokacija za vektor, veća od trenutne. Svi se stari elementi vektora kopiraju na novu lokaciju i dodaje se novi element. Memorija koju je vektor ranije koristio se dealocira.
Elemente možemo ubacivati u vektor na bilo koje mjestu pomoću metode insert()
. Ta operacija ima linearnu
složenost. Za brisanje koristimo metodu erase()
.
std::vector<int> b; // b = prazan vektor
b.push_back(1);
b.push_back(2);
b.push_back(3); // b = 1,2,3
b.pop_back(); // b = 1,2
auto it = b.begin();
it++;
b.insert(it,3); // b = 1,3,2
--it;
auto it_beg = b.erase(it); // b = 3,2
assert(*it_beg == 3);
Operacija | Značenje |
---|---|
|
Dodaj element |
|
Ukloni zadnji element iz spremnika (ne vraća ga) |
|
Ubaci kopiju elemente |
|
Ubaci |
|
Ubaci kopiju elemenata iz raspona |
|
Ubaci kopiju elemenata inicijalizacijske liste |
|
Obriši element na koji pokazuje iterator |
|
Obriši elemente u rasponu |
|
Obriši sve elemente u spremniku. |
std::vector<T>
- brisanje elementa
Brisanje elementa vrši metoda erase()
. Metoda uzima iterator koji pokazuje na
element za brisanje ili raspon iteratora koji određuje elemente za brisanje:
std::vector<unsigned int> b(7);
for(unsigned int i = 0; i < b.size(); ++i)
b[i] = 2*i+1;
// sada je b = 1,3,5,7,9,11,13
b.erase(b.begin()+1); // obriši drugi element, b = 1,5,7,9,11,13
b.erase(b.begin(), b.begin()+3); // obriši prva tri elementa, b = 9,11,13
Brisanje elementa zadanog po vrijednosti vršimo pomoću std::remove
algoritma
(zaglavlje <algorithm>
) i metode erase()
koristeći erase-remove idiom.
Algoritam std::remove(it_begin, it_end, val)
pronalazi val
u rasponu [it_begin, it_end)
izbacuje ga iz tog raspona i vraća iterator koji pokazuje na kraj skraćenog raspona.
Kako algoritmi ne mogu vršiti operacije na spremniku on ne izbacuje element iz spremnika. Za taj dio
posla moramo pozvati erase()
metodu.
// remove-erase idiom.
auto it = std::remove(b.begin(), b.end(), 11); // it je novi kraj raspona
b.erase(it, b.end()); // b = 9,13
Zadnji element se može ukloniti metodom pop_back()
.
Svi elementi se mogu obrisati metodom clear()
:
vd.clear();
assert(vd.empty());
std::vector<T>
- smještavanje elementa
Kada se element u vektor ubacuje pomoću push_back()
metode vrši se konstrukcija elementa
kopiranjem. Element se može direktno konstruirati u vektoru pomoću emplace_back()
metode. Ona konstruira element iza zadnjeg elementa u vektoru pozivajući direktno konstruktor;
metoda zato uzima argumente konstruktora.
struct X{
X(int i = 0, double y = 0.0) : ii(i), yy(y) {}
int ii;
double yy;
};
X xa, xb;
std::vector<X> xvec;
xvec.push_back(xa); // kopiranje
xvec.push_back(xb); // kopiranje
xvec.emplace_back(1, 2.2); // konstrukcija
std::cout << xvec.at(2).yy << std::endl; // ispisuje 2.2
Operacija | Značenje |
---|---|
|
Ubacuje kopiju elementa inicijaliziranog pomoću |
|
Dodaje kopiju elementa inicijaliziranog pomoću |
Svojstva vektora
-
Vektor čuva elemente na uzastopnim lokacijama u jednom bloku memorije i stoga je memorijski efikasan.
-
Brine o dinamičkoj alokaciji i dealokaciji memorije te predstavlja vektor neograničene veličine.
-
Pri ekspanziji može biti realociran što obezvrijeđuje iteratore i reference na elemente.
-
Direktno može dohvatiti svaki element u konstantnom vremenu.
-
Daje iteratore izravnog dohvata.
-
Efikasan je kod ubacivanja i brisanja elemenata na kraju vektora.
-
Nije efikasan kod ubacivanja i brisanja elemenata na početku i u sredini vektora.
-
Ako je izbačen izuzetak u metodi
push_back()
onda ta metoda nema efekta;pop_back()
ne izbacuje izuzetke. -
Ako tip podatka koji koristimo u vektoru ne izbacuje izuzetke pri kopiranju i premještanju, onda sve operacije na vektoru imaju svojstvo da su ili uspješne ili nemaju efekta.
std::vector<bool>
Specijalizacija vektora std::vector<bool>
je specifična jer optimizira dinamički
alociranu memoriju. Umjesto da za svaki element vektora alocira jedan bool
(jedan bajt)
std::vector<bool>
svaki svoj element reprezentira jednim bitom.
Posljedica toga je da std::vector<bool>
nema sva svojstva pravog sekvencijalnog spremnika.
Pored toga, ima metodu flip()
koja mijenja poljedini bit iz nule u jedan ili obratno.
std::vector<bool> vec(12, true);
// Ispis vektora.
for(auto x : vec) std::cout << x; // 111111111111
std::cout << std::endl;
// Promijeni sve jedinice u nulu i obratno.
vec.flip(); // 0->1, 1->0
for(auto x : vec) std::cout << x; // 000000000000
std::cout << std::endl;
// Promijeni samo 4. i 11. element
vec[3].flip();
vec[10].flip();
vec[0] = true; // standardno prodruživanje
for(auto x : vec) std::cout << x; // 100100000010
std::cout << std::endl;
// Dodaj nove elemente na kraj spremnika.
vec.push_back(true);
vec.push_back(false);
vec.push_back(true);
for(auto x : vec) std::cout << x; // 100100000010101
std::cout << std::endl;
Operator indeksiranja vraća proxy objekt a ne referencu na bool
tako da je sljedeći kod neispravan:
auto & v = vec[0]; // greška, [] vraća privremeni proxy objekt
auto & vv = *vec.begin(); // greška, izraz vraća privremeni proxy objekt
std::bitset<N>
Ako trebamo vektor bitova konačne duljine onda je bolje koristiti std::bitset<N>
.
Spremnik bitset
kao parametar predloška uzima broj bitova, a kao i std::vector<bool>
pamti bitove na optimalan
način. Sučelje bitset
-a se može vidjeti u ovom primjeru:
#include <iostream>
#include <bitset>
int main() {
std::bitset<32> flags; // flags = 00000000000000000000000000000000
std::cout << "flags = "
<< flags.to_string() // to_string() pretvara bitset u string
<< std::endl; // nula i jedinica
flags.set(11); // postavi dvanaesti bit
flags[10] = true; // postavi jedanaesti bit
flags.flip(9); // deseti bit promijeni iz nule u jedinicu
// sada je flags = 00000000000000000000111000000000
flags.flip(); // invertiraj sve bitove
// sada je flags = 11111111111111111111000111111111
std::bitset<4> fl1("0011"); // konstruiraj bitset iz stringa
std::bitset<4> fl2("0101");
// Logičke operacije nad bitset objektima
std::cout << "fl1 & fl2: " << (fl1 & fl2) << std::endl; // fl1 & fl2: 0001
std::cout << "fl1 | fl2: " << (fl1 | fl2) << std::endl; // fl1 | fl2: 0111
std::cout << "fl1 ^ fl2: " << (fl1 ^ fl2) << std::endl; // fl1 ^ fl2: 0110
// Ispiši vrijednost kao broj
std::cout << fl1.to_ulong() << std::endl; // ispisuje 3
return 0;
}
std::deque<T>
Da bismo mogli koristiti deque
(double-ended queue) treba uključiti njegovu datoteku zaglavlja:
#include <deque>
Spremnik deque
je vrlo sličan vektoru. Dinamički alocira i dealocira memoriju
te dozvoljava direktan pristup elementima pomoću operatora indeksiranja i iteratora.
Za razliku od vektora deque
dozvoljava efikasno ubacivanje elemenata na oba kraja spremnika.
Stoga ima, pored metode push_back()
i metodu push_front()
; zatim pop_back()
i pop_front()
te
emplace_back()
i emplace_front()
.
deque
se obično implementira kao više blokova memorije koji ne čine nužno jedan kontinuirani
blok memorije, kao što je to slučaj kod vektora. Stoga je memorijski manje efikasan od vektora.
std::deque<float> dec;
dec.push_back(1.0);
dec.push_front(-1.0);
std::cout << dec[0] << std::endl; // ispisuje -1.0
std::cout << dec[1] << std::endl; // ispisuje 1.0
-
deque
ima gotovo isto sučelje kao ivector
. Iznimka je što nema metodecapacity()
ireserve()
. Ima metodepush_front()
ipop_front()
koje vektor nema. Sve ostale metode su iste kao i kod vektora. -
Ubacivanje i brisanje elementa na oba kraja spremnika je brzo, ali je u sredini spremnika sporo.
-
Daje iteratore izravnog dohvata.
-
Iteratori su mu sporiji od vektorovih iteratora. Svako ubacivanje elementa i svako brisanje elementa obezvrijeđuje sve iteratore i reference na elemente spremnika.
-
Ako je izbačen izuzetak u metodi
push_back()
ilipush_front()
onda ta metoda nema efekta;pop_back()
ipop_front()
ne izbacuju izuzetke. -
Kao i kod vektora, ako tip podatka koji koristimo ne izbacuje izuzetke pri kopiranju i premještanju, onda sve operacije na
deque
-u imaju svojstvo da su ili uspješne ili nemaju efekta.
Spremnik std::deque<T>
treba koristiti samo kada je bitno da se elementi mogu ubacivati/izbacivati
na oba kraja spremnika (dakle, kada nam treba red). Bez toga std::deque<T>
nema nikakve
prednosti pred vektorom.
std::list<T>
- konstrukcija
Da bismo mogli koristiti list
treba uključiti njegovu datoteku zaglavlja:
#include <list>
Ovaj spremnik predstavlja dvostruko povezanu listu. Stoga nudi samo dvosmjerne iteratore,
a ne iteratore izravnog dohvata kao vector
i deque
.
Elementi liste mogu se nalaziti bilo gdje u memoriji, a povezani su pokazivačima, te stoga lista nije memorijski efikasna.
Lista ima iste konstruktore kao i vector
:
std::list<int> li1; // prazna lista
std::list<char> lc1(16); // lista od 16 elemenata (inicijalizirani nulama)
std::list<float> lf1(8, 1.2f); // lista od 8 elemenata inicijaliziranih sa 1.2f
std::list<int> li2(lf1.begin(), lf1.end()); // lista inicijalizirana elementima
// drugog spremnika
std::list<int> li3{1,2,3,4,5}; // inicijalizacija navođenjem elemenata
Operacija | Značenje |
---|---|
|
Dodijeljeni konstruktor; kreira praznu listu |
|
Konstruktor kopije |
|
Konstruktor kopije |
|
Konstruktor kopije premještanjem |
|
Konstruktor kopije premještanjem |
|
Kreira listu od |
|
Kreira listu od |
|
Kreira listu inicijaliziran elementima iz raspona |
|
Kreira listu inicijaliziran elementima iz inicijalizacijske liste |
|
Kreira listu inicijaliziran elementima iz inicijalizacijske liste |
|
Destruktor |
Kao i kod ostalih sekvencijalnih spremnika postoje sljedeće operacije koje ne mijenjaju stanje spremika.
Operacija | Značenje |
---|---|
|
Vraća |
|
Vraća broj elemenata u listi |
|
Vraća maksimalni mogući broj elemenata u listi |
|
Vraća |
|
Vraća |
Metode capacity()
, reserve()
i shrink_to_fit()
ne postoje jer nema potrebe za njima.
Operacije na čitavom spremniku su iste kao i kod ostalih sekvencijalnih spremnika:
Operacija | Značenje |
---|---|
|
Pridruži sve elemente spremika |
|
Pridruži premještanjem sve elemente spremika |
|
Pridruži sve elemente inicijalizacijske liste |
|
Pridruži |
|
Pridruži elemente iz raspona |
|
Pridruži sve elemente inicijalizacijske liste |
|
Zamjeni sadržaj spremnika |
|
Zamjeni sadržaj spremnika |
std::list<T>
- dohvat elemenata
Dohvat elemenata ide kroz pokazivače (nema operatora dohvata []
) te kroz metode front()
i
back()
.
// Dohvat elemenata kroz pokazivač
for(auto it=lf1.begin(); it != lf1.end(); ++it)
std::cout << *it << ",";
std::cout << std::endl;
// range-for petlja
for(auto x : li2)
std::cout << x << ",";
std::cout << std::endl;
if(!li3.empty()){ // ili li3.size() != 0
std::cout << li3.front() << std::endl; // nema provjere da li
std::cout << li3.back() << std::endl; // element postoji
}
Operacija | Značenje |
---|---|
|
Vrati prvi element (ne provjerava je li lista prazna) |
|
Vrati zadnji element (ne provjerava je li lista prazna) |
Dohvat proizvoljnog elementa ide kroz iteratore. Funkcije koje vraćaju iteratore su iste kao i kod ostalih sekvencijalnih spremnika. Razlika je ovdje ta što vraćaju dvosmjerni iterator koji nije iterator izravnog pristupa.
Operacija | Značenje |
---|---|
|
Vraća iterator na poziciju prvog elementa |
|
Vraća iterator na poziciju iza zadnjeg elementa |
|
Vraća konstantan iterator na poziciju prvog elementa |
|
Vraća konstantan iterator na poziciju iza zadnjeg elementa |
|
Vraća reverzni iterator na poziciju prvog elementa reverznog niza |
|
Vraća reverzni iterator na poziciju iza zadnjeg elementa reverznog niza |
|
Vraća konstantan reverzni iterator na poziciju prvog elementa reverznog niza |
|
Vraća konstantan reverzni iterator na poziciju iza zadnjeg elementa reverznog niza |
std::list<T>
- ubacivanje i izbacivanje elemenata
Za ubacivanje koristimo push_back()
, push_front()
i insert()
(koji ima više verzija).
Za izbacivanje koristimo pop_back()
, pop_front()
i remove()
.
Ubacivanje i izbacivanje je efikasno na svakom mjestu.
Radi efikasnosti umjesto remove()
algoritma uvijek treba koristiti remove()
metodu klase std::list<T>
.
// li2 = {1,2,3,4,5}
li3.push_back(6); // 1,2,3,4,5,6
li3.push_front(0); // 0,1,2,3,4,5,6
li3.remove(4); // ukloni sve elemente jednake 4 -> 0,1,2,3,5,6
li3.remove_if([](int x){ return x % 2; }); // ukloni neparne elemente
// 0,2,6
li3.pop_back(); // 0,2
li3.pop_front(); // 2
li3.insert(li3.end(), 2, 7); // ubaci na kraj liste dva puta element 7.
// 2,7,7
li3.insert(std::prev(li3.end()), 17); // ubaci 17 ispred zadnjeg elementa
// 2,7,17,7
li3.resize(5); // odredi novu veličinu liste. Nove elemente stavi na nulu.
// 2,7,17,7,0
Metoda remove()
eliminira elemente zadane vrijednosti. Metoda erase()
eleiminira element na koji
pokazuje iterator (ili sve elemente iz raspona).
li3.erase(std::next(li3.begin())); // obriši drugi element -> 2,17,7,0
li3.erase(std::next(li3.begin(),2), li3.end()); // obriši od trećeg do
// zadnjeg elementa -> 2,17
Operacija | Značenje |
---|---|
|
Dodaj element |
|
Ukloni zadnji element iz spremnika (ne vraća ga) |
|
Dodaj element |
|
Ukloni prvi element iz spremnika (ne vraća ga) |
|
Ubaci kopiju elemente |
|
Ubaci |
|
Ubaci kopiju elemenata iz raspona |
|
Ubaci kopiju elemenata inicijalizacijske liste |
|
Obriši element na koji pokazuje iterator |
|
Obriši elemente u rasponu |
|
Obriši sve elemente u spremniku. |
Prisutne su i operacije smještavanja elementa u spremnik. Metode emplace
uzimaju argumnete konstruktora i
konstruiraju element na odgovarajućem mjestu.
Operacija | Značenje |
---|---|
|
Ubacuje kopiju elementa inicijaliziranog pomoću |
|
Dodaje kopiju elementa inicijaliziranog pomoću |
|
Dodaje kopiju elementa inicijaliziranog pomoću |
Lista ima iste resize
metode kao i vektor:
Operacija | Značenje |
---|---|
|
Promijeni broj elemenata spremnika na |
|
Promijeni broj elemenata spremnika na |
Konačno remove
algoritam je implementiran u samoj listi i efikasniji je od generalnog remove
algoritma.
Operacija | Značenje |
---|---|
|
Ukloni iz spremnika sve elemente koji imaju vrijednost |
|
Ukloni iz spremnika sve elemente za koje |
Algoritmi u std::list<T>
Klasa std::list<T>
implementira neke algoritme kao funkcije članice jer na taj način postiže
veću efikasnost od generalih algoritama, ili se generalni algoritmi ne mogu primijeniti jer traže
iteratore izravnog dohvata (kao sort
).
Algoritmi implementirani u klasi list
su efikasniji od generalih algoritama jer manipuliraju samo sa pokazivačim.
Na primjer, algoritam sort
sortira listu, unique
eliminira uzastopne duplikate, a reverse
invertira listu:
list<int> li{2,5,1,7,5,4,9,7};
li.sort(); // 1,2,4,5,5,7,7,9,
li.unique(); // 1,2,4,5,7,9,
li.reverse(); // 9,7,5,4,2,1,
li.reverse(); // nazad na 1,2,4,5,7,9,
Algoritam merge
vrši spajanje dvije sortirane liste u jednu sortiranu list.
Pri tom nema kopiranja elemenata i lista koja se uključuje u polaznu listu ostaje prazna.
list<int> li2{5, 8, 4, 11};
li2.sort();
li.merge(li2); // 1,2,4,4,5,5,7,8,9,11,
assert(li2.empty());
Metoda splice
dozvoljava da se elementi jednog spremnika transferiraju u drugi na danom mjestu.
Pri tome nema kopiranja elemenata već se samo manipuliraju pokazivači. Može se transferirati čitav
spremnik, samo jedan element ili raspon elemenata.
// li2 je prazan
li2.push_back(17);
li2.push_back(18);
li2.push_back(19);
li.splice(next(li.begin(),3), li2); //1,2,4,17,18,19,4,5,5,7,8,9,11,
Transfer se dešava ispred pokazivača next(li.begin(),3)
, što znači ispred četvrtog elementa.
Operacija | Značenje |
---|---|
|
Ukloni sve duplikate kod konsekutivnih elemenata iste vrijednosti |
|
Ukloni sve duplikate kod konsekutivnih elemenata za koje |
|
Premjesti sve elemente iz |
|
Premjesti element na poziciji |
|
Premjesti sve elemente iz raspona |
|
Sortiraj elemente spremnika pomoću operatora < |
|
Sortiraj elemente spremnika pomoću operatora uspoređivanja |
|
Uz pretpostavku da su oba spremnika sortirana, premjesti sve elemente iz |
|
Uz pretpostavku da su oba spremnika sadrže elemente sortirane pomoću operatora uspoređivanja |
|
Invertiraj poredak svih elemenata |
std::forward_list<T>
Da bismo mogli koristiti forward_list
treba uključiti njegovu datoteku zaglavlja:
#include <forward_list>
Ovaj je spremnik uveden standardom iz 2011. godine i predstavlja jednostruko povezanu list.
On zauzima manje memorije od std::list<T>
spremnika i nudi podskup funkcionalnosti koju nudi std::list<T>
.
std::forward_list<T>
ima sljedeća svojstva:
-
Nudi samo jednosmjerne iteratore;
-
Nema metode
size()
,push_back()
,back()
ipop_back()
; -
Metode za konstrukciju su iste kao i kod dvostruko povezane liste.
forward_list<int> fli{3,5,1,6,4,6};
cout << "fli size = " // nema metode size,
<< distance(fli.begin(), fli.end()) << endl;
// distance je u zaglavlju <iterator>
std::forward_list<T>
insert metode
insert
metode u sekvencijalnim spremnicima vrše insertiranje ispred mjesta na kojem želimo
ubacivanje. Kod std::forward_list<T>
to nije moguće zbog nemogućnosti kretanja unazad.
Stoga je metoda insert
preimenovana u insert_after
i ubacuje element(e) iz lokacije na koju
pokazuje dani iterator. Da bi bilo moguće ubacivati na početak liste metoda before_begin()
vraća pokazivač na jedno mjesto prije prvog u spremniku.
list<int> li{0,0,0};
fli.insert_after(fli.before_begin(), li.begin(), li.end());
// dobivamo 0,0,0,3,5,1,6,4,6,
Na isti način su metode emplace
i erase
zamijenjene sa emplace_after
i erase_after
:
fli.erase_after(fli.begin()); // 0,2,3,5,1,6,4,6,
fli.erase_after(next(fli.begin(),5), fli.end()); // 0,2,3,5,1,6,
erase_after
briše element nakon onog na koji pokazuje iterator. Verzija koja uzima raspon
iteratora briše sve elemente u rasponu osim prvog i zadnjeg.
Metode splice
su također preimenovane u splice_after
. Ova metoda prebacuje elemente iz jednog
forward_list<>
spremnika u drugi forward_list<>
, iza dane lokacije:
forward_list<int> fli2{2,2,2};
fli.splice_after(next(fli.begin(),3), fli2); // 0,2,3,5,2,2,2,1,6,
Kao i std::list<T>
, spremnik std::forward_list<T>
ima algoritme sort
, unique
, reverse
, merge
i remove
.