Vektori (C++)

Vector ( std::vector<T>) on standardi C++:n yleinen ohjelmointimalli, joka toteuttaa dynaamisen taulukon .

Malli vectorsijaitsee otsikkotiedostossa <vector>. Kuten kaikki vakiokomponentit , se sijaitsee . Tämä käyttöliittymä emuloi tavallisen Cstd - taulukon toimintaa (esimerkiksi nopeaa satunnaista pääsyä elementteihin) sekä joitain lisäominaisuuksia, kuten vektorin automaattista koon muutosta, kun elementtejä lisätään tai poistetaan.

Vektorin kaikkien elementtien on oltava samaa tyyppiä. Et esimerkiksi voi tallentaa char- ja int -tietoja yhdessä samaan vektoriinstanssiin. Luokassa vector on vakiomenetelmät elementtien käyttämiseen, elementtien lisäämiseen ja poistamiseen sekä tallennettavien elementtien määrän hankkimiseen.

Alustus

Vektori voidaan alustaa millä tahansa tyypillä, jolla on kopiokonstruktori ja joka on määritelty operator=ja täyttää seuraavat ehdot [1] :

Ilmaisu palautustyyppi Kunto
t = u T& tvastaavau
T(t) tvastaavaT(t)
T(u) uvastaavaT(u)
&u const T* näyttää osoitettau
t.~T()

Tässä T on tyyppi, jolla vektori alustetaan, t on tyypin muuttuja T, u on tyypin muuttuja (mahdollisesti const) T.

Esimerkiksi:

vektori < int > myVector ; // Tyhjä vektori elementeistä, joiden tyyppi on int vector < float > myVector ( 10 ); // 10:n vektori leijuttaa vektorin < char > myVector ( 5 , ' ' ); // Vektori, joka koostuu 5 välilyönnistä luokka T { ... }; int n = 10 ; vektori < T > myVektori ( n ); // 10 mukautetun tyypin T elementin vektori

Elementtien käyttö

Vektorin yksittäistä elementtiä voidaan käyttää alla olevassa taulukossa kuvattujen toimintojen avulla. C- ja C++- käytännön mukaan ensimmäisellä elementillä on indeksi 0ja viimeisellä elementillä indeksi size() - 1[2] .

Ilmaisu palautustyyppi Rajatarkastus?
v.at(i) T&tai const T&elementille i Poikkeuksen tekeminen on mahdollistaout_of_range
v[i] T&tai const T&elementille i Määrittelemätön käyttäytyminen milloini >= v.size()
v.front() T&tai const T&ensimmäiselle elementille Määrittelemätön käyttäytyminen milloinv.empty() == true
v.back() T&tai const T&viimeiselle elementille Määrittelemätön käyttäytyminen milloinv.empty() == true

Missä v on objekti tyyppiä (mahdollisesti const) vector<T>ja i on vektorin vaaditun elementin indeksi.

Jotkut menetelmät

Luokka vector on kontti. C++-standardin mukaan minkä tahansa säiliön tulee sisältää menetelmät begin(), end(), size(), max_size(), empty(), ja swap().

Alla on lyhyt luettelo käytettävissä olevista menetelmistä kuvauksen ja monimutkaisuuden osoituksella .

Menetelmä Kuvaus Monimutkaisuus
Rakentajat vector::vector Oletuskonstruktori. Ei ota argumentteja, luo uuden vektorin esiintymän O(1)(suorittaa vakioajassa)
vector::vector(const vector& c) Oletuskopiointikonstruktori. Luo vektorista kopionc O(n)(toimii lineaarisessa ajassa, joka on verrannollinen vektorin kokoon c)
vector::vector(size_type n, const T& val = T()) Luo vektorin nobjekteista. Jos valilmoitettu, jokainen näistä objekteista alustetaan sen arvolla; muuten objektit saavat oletuskonstruktoriarvon tyyppiä T. O(n)
vector::vector(input_iterator start, input_iterator end) Luo elementtien vektorin välillä startjaend O(n)
Tuhoaja vector::~vector Tuhoaa vektorin ja sen elementit
Operaattorit vector::operator= Kopioi yhden vektorin arvon toiseen. O(n)
vector::operator== Kahden vektorin vertailu O(n)
Pääsy
elementteihin
vector::at Elementin käyttö rajojen ulkopuolisella tarkistuksella O(1)
vector::operator[] Tietyn elementin käyttö O(1)
vector::front Pääsy ensimmäiseen elementtiin O(1)
vector::back Pääsy viimeiseen elementtiin O(1)
Iteraattorit vector::begin Palauttaa iteraattorin vektorin ensimmäiseen elementtiin O(1)
vector::end Palauttaa iteraattorin vektorin viimeisen elementin jälkeen O(1)
vector::rbegin Palaa reverse_iteratornykyisen vektorin loppuun. O(1)
vector::rend Palaa reverse_iteratorvektorin alkuun. O(1)
Työskentely
vektorin koon kanssa
vector::empty Palauttaa true, jos vektori on tyhjä O(1)
vector::size Palauttaa vektorin elementtien määrän O(1)
vector::max_size Palauttaa suurimman mahdollisen määrän elementtejä vektorissa O(1)
vector::reserve Asettaa vektorin elementtien vähimmäismäärän O(n)
vector::capacity Palauttaa elementtien määrän, jonka vektori voi sisältää, ennen kuin sen on varattava lisää tilaa. O(1)
vector::shrink_to_fit Vähentää käytetyn muistin määrää vapauttamalla käyttämätöntä muistia ( C++11 ) O(1)
Muokkaimet vector::clear Poistaa kaikki vektorin elementit O(n)
vector::insert Elementtien lisääminen vektoriin Lisäys lopussa edellyttäen, että muistia ei jaeta uudelleen - O(1), mielivaltaiseen paikkaan -O(n)
vector::erase Poistaa määritetyt vektorielementit (yksi tai useampi) O(n)
vector::push_back Elementin lisääminen vektorin loppuun O(1)
vector::pop_back Poista vektorin viimeinen elementti O(1)
vector::resize Muuttaa vektorin kokoa annetulla määrällä O(n)
vector::swap Vaihda kahden vektorin sisältö O(1)
Muut menetelmät vector::assign Yhdistää annetut arvot vektoriin O(n), jos haluttu vektorin koko on asetettu, O(n*log(n))muistia uudelleenallokoitaessa
vector::get_allocator Palauttaa muistin varaamiseen käytetyn allokaattorin O(1)

Iteraattorit

Yllä kuvattujen suorien elementtien käyttötoimintojen lisäksi vektorin elementteihin pääsee käsiksi iteraattoreilla .

Iteraattoreita käytetään yleensä pareittain, joista toista käytetään osoittamaan nykyinen iteraatio ja toista käytetään osoittamaan säiliön loppua. Iteraattorit luodaan vakiomenetelmillä, begin()kuten end(). Funktio begin()palauttaa osoittimen ensimmäiseen elementtiin ja palauttaa osoittimen viimeistä seuraavaan end() kuvitteelliseen olemattomaan elementtiin.

Vektori käyttää ominaisuuksiltaan monipuolisinta iteraattorityyppiä, RandomAccessIterator (random access iterator), jonka avulla voit kulkea kontin läpi missä tahansa järjestyksessä sekä muuttaa vektorin sisältöä läpikulkuprosessin aikana. Jos vektori kuitenkin muuttuu, iteraattori voi muuttua virheelliseksi.

Esimerkki vektorielementtien summan laskemisesta iteraattoreilla:

#include <iostream> #sisällytä <vektori> #include <iteraattori> käyttäen nimiavaruutta std ; int main () { vektori < int > vektori ; vektori < int >:: iteraattori the_iterator ;     for ( int i = 0 ; i < 10 ; i ++ ) {         the_vektori . push_back ( i );     }     kokonaissumma = 0 ; _     the_iterator = the_vektori . alkaa ();     while ( the_iterator != the_vector . end ()) {       yhteensä += * iteraattori ++ ; }           cout << "summa= " << yhteensä << endl ;     paluu 0 ; } vektori < int > vektori ; vektori < int >:: iteraattori the_iterator ; for ( int i = 0 ; i < 10 ; i ++ ) { the_vektori . push_back ( i ); } kokonaissumma = 0 ; _ the_iterator = the_vektori . alkaa (); while ( the_iterator != the_vector . end ()) { yhteensä += * iteraattori ; /* Huomaa, että elementtiin pääsee käsiksi poistamalla viittaus iteraattoriin */ ++ the_iterator ; } cout << "Yhteensä=" << yhteensä << endl ;

Tulos:
Yhteensä = 45

Vektorin äänenvoimakkuus ja koon muuttaminen

Tyypillinen vektoritoteutus on osoitin dynaamiseen taulukkoon. Vektorin koko on elementtien todellinen lukumäärä, ja koko on sen käyttämän muistin määrä.

Jos uusia elementtejä lisättäessä vektoriin sen koko tulee suurempi kuin sen tilavuus, muisti varataan uudelleen. Tyypillisesti tämä saa vektorin varaamaan uuden tallennusalueen, siirtämään elementit ja vapauttamaan vanhat alueet uuteen muistipaikkaan.

Koska elementtien osoitteet muuttuvat tämän prosessin aikana, kaikki vektorin elementtien viittaukset tai iteraattorit voivat muuttua kelpaamattomiksi. Virheellisten viitteiden käyttö johtaa määrittelemättömään toimintaan. Esimerkki:

#sisällytä <vektori> int main () { std :: vektori < int > v ( 1 ); // Luo vektori yhdellä int-elementillä, jonka arvo on 0 int & ensimmäinen = * v . alkaa (); // Luo linkki ensimmäiseen elementtiin v . insert ( v . end (), v . kapasiteetti (), 0 ); // Lisää uusia elementtejä int i = ensimmäinen ; // Määrittelemätön käyttäytyminen. Linkki ei ehkä ole kelvollinen }

Menetelmää reserve()käytetään turhan muistin uudelleenallokoinnin estämiseen. Kutsumisen reserve(n)jälkeen vektorin koko on taatusti vähintään n. Esimerkki:

#sisällytä <vektori> int main () { std :: vektori < int > v ( 1 ); // Luo vektori, joka koostuu yhdestä int-tyypin elementistä, jonka arvo on 0 v . reservi ( 10 ); // Varaa tila int & first = * v . alkaa (); // Luo linkki ensimmäiseen elementtiin v . lisää ( v . loppu (), 5 , 0 ); // Elementtien lisääminen vektoriin int i = ensimmäinen ; // OK, koska muistia ei jaettu uudelleen }

Vektori ylläpitää tiettyä elementtijärjestystä siten, että kun uusi elementti lisätään vektorin alkuun tai keskelle, seuraavat elementit siirretään taaksepäin niiden osoitusoperaattorin ja kopiokonstruktorin suhteen. Siksi lisäyskohdan jälkeiset elementtiviittaukset ja iteraattorit mitätöidään. Esimerkki:

#sisällytä <vektori> int main () { std :: vektori < int > v ( 2 ); // Luo vektori, joka koostuu kahdesta Int-tyypin elementistä // Luo viittaukset molempiin elementteihin int & first = v . edessä (); int & last = v . takaisin (); v . insert ( v . alkaa () + 1 , 1 , 1 ); // Lisää uusia elementtejä vektorin keskelle int i = ensimmäinen ; // Määrittämätön toiminta, jos lisäys aiheutti uudelleenallokoinnin int j = viimeinen ; // Määrittelemätön toiminta C++-standardin §23.2.4.3/1 mukaisesti }

vektori<bool> erikoisala

C++-standardikirjasto määrittelee vektorimallien erikoistumisen bool. Erikoistumisen mukaan vektorin tulee pakata elementit siten, että jokainen tyypin elementti bооlkäyttää vain yhtä bittiä muistia [3] . Monet kutsuvat tätä bugiksi [4] [5] , koska se ei täytä C++ Standard Libraryvector<bool> -säilön vaatimuksia . Säilön on esimerkiksi oltava tosi tyyppiä T. Tämä ei päde c:n tapauksessa , joka on paikkamerkki , joka muunnetaan muotoon [6] . Sitä paitsi, ei anna viittauksella. C++-standardointikomitean ja kirjaston kehitystiimin välillä on sopimus, jonka mukaan se tulee poistaa käytöstä ja sitten poistaa vakiokirjastosta , jolloin toiminnallisuus palautetaan, mutta eri nimellä. [7]<T>::referencelvaluevector<bool>::referenceboolvector<bool>::iteratorbool&vector<bool>

Käyttö

Vektoria käyttävien C++- ohjelmien tulee sisältää otsikkotiedosto <vector>:

#sisällytä <vektori> // Tämän jälkeen voit alustaa muuttujan std :: vector < T > myVector ;

Tässä T - säilöön tallennettavien tietojen tyyppi ja myVector - käytettävä muuttuja. Tvoi olla mikä tahansa tietotyyppi, mukaan lukien käyttäjän määrittämä tietotyyppi.

Array substituutio

C : ssä ja C++ : ssa taulukko  on dataa vierekkäisissä muistilohkoissa. Jokaiselle lohkolle määritetään sitten indeksi, ja kunkin lohkon sisältö voidaan löytää tuntemalla sen indeksi. Kaikkien taulukon elementtien on oltava samaa tyyppiä.

Vektori on samanlainen kuin dynaaminen taulukko, mutta vektori voi muuttaa itsensä kokoa. Muistia ei myöskään tarvitse vapauttaa manuaalisesti.

Koska vektorin elementit tallennetaan vierekkäin, vektorin ensimmäisen elementin osoite voidaan välittää funktiolle taulukkona (osoitin ensimmäiseen elementtiin). Seuraava esimerkki havainnollistaa, kuinka vektoria voidaan käyttää C-standardin kirjastofunktioiden memcpyja printf:

#include <cstring> // memcpy #sisällytä <vektori> #include <cstdio> // printf int main () { käyttäen nimiavaruutta std ; const char arr [] = "1234567890" ; // Luo vektori, jossa on 11 '\0'- vektoria < char > vec ( sizeof arr ); // Kopioi 11 elementtiä tyyppiä 'char' vektorimuistiin ( vec . data ( ), arr , sizeof arr ); // Tulostaa "1234567890" printf ( "%s" , vec . data ()); }

Huomaa, että memcpyja printfei ole suositeltavaa käyttää turvallisempia vaihtoehtoja C++ Standard Librarysta

Käyttöesimerkki

Seuraava esimerkki esittelee erilaisia ​​tekniikoita, jotka sisältävät vektorin ja C++-standardikirjastoalgoritmeja , kuten sekoittamisen, lajittelun, suurimman elementin löytämisen ja poistamisen vektorista käyttämällä pyyhi-poisto-idiomia.

#include <iostream> #sisällytä <vektori> #include <algoritm> // sort, max_element, random_shuffle, remove_if, low_bound #include <functional> // suurempi, bind2nd // Käytetään mukavuuden vuoksi. Käytä oikeissa ohjelmissa varovasti käyttämällä nimiavaruutta std ; int main () { int arr [ 4 ] = { 1 , 2 , 3 , 4 }; // Alusta vektori käyttämällä taulukkovektoria < int > numerot ( arr , arr + 4 ); // Lisää numeroita lukuvektoriin . push_back ( 5 ); numerot . push_back ( 6 ); numerot . push_back ( 7 ); numerot . push_back ( 8 ); // Nyt vektori näyttää tältä: {1, 2, 3, 4, 5, 6, 7, 8} // Sekoita elementit satunnaisesti random_shuffle ( numerot . alkaa (), numerot . loppu ()); // Hanki maksimielementti, monimutkaisuus O(n) vektori < int >:: const_iterator suurin = max_element ( numerot . alkaa (), numerot . loppu () ); cout << "suurin elementti" << * suurin << endl ; cout << "Tämän elementin indeksi" << suurimmat - numerot . alkaa () << endl ; // Lajittele alkiot, monimutkaisuus O(n log n) lajittelu ( numerot . alku (), numerot . loppu ()); // Etsi luvun 5 sijainti vektorista, kompleksisuus O(log n) vektori < int >:: const_iterator five = alaraja ( numerot . alkaa (), numerot . loppu (), 5 ); cout << "Numero 5 on indeksissä " << viisi numeroa . alkaa () << endl ; // Poista kaikki elementit, jotka ovat suurempia kuin 4 numeroa . erase ( poista_jos ( numerot . alkaa (), numerot . loppu (), bind2nd ( suurempi < int > (), 4 )), numerot . loppu () ); // Tulosta loput for ( vektori < int >:: const_iterator it = numerot . alkaa (); it != numerot . end (); ++ it ) { cout << * se << ' ' ; } paluu 0 ; }

Tulos sisältää:

Suurin elementti 8

Tämän elementin indeksi on 6 (toteutuksesta riippuvainen)

Numero 5 sijaitsee indeksin 4 alla

1 2 3 4

Esimerkki 2-ulotteisesta dynaamisesta vektorista sekä esimerkki sen käyttämisestä ja muokkaamisesta

typedef std :: vektori < std :: vektori < int > > pxMP ; void function () { int kokoX , kokoY ; // määritä koko lennossa. pxMP pxMap ( kokoX , std :: vektori < int > ( kokoY )); // joukko X/Y-pikseleitä 0.1. pxMap [ 0 ][ 5 ] = 1 ; /* pääsy */ // poista pxMap -sovelluksen vasen ja oikea sarake . pop_back (); pxMap . pyyhkiä ( pxMap.begin ( ) ); // poista ylä- ja alarivit kaikista sarakkeista, luo ensin työkaluja tätä varten: std :: vector < std :: vector < int > >:: iterator iterlvl2 ; // iteraattori toiselle ulottuvuudelle. std :: vektori < int >:: iteraattori iterlvl1 ; // ensimmäisen ulottuvuuden iteraattori // Siirry syvälle kohteelle ( iterlvl2 = pxMap . begin (); iterlvl2 != pxMap . end (); iterlvl2 ++ ) { iterlvl1 = ( * iterlvl2 ). alkaa (); // Vain esittelytarkoituksiin ( * iterlvl2 ). pop_back (); ( * iterlvl2 ). erase (( * iterlvl2 ) .begin ()); // Missä olemme? kokoY = ( * iterlvl2 ). koko (); // Aseta kokoY, kun olemme tällä tasolla. Silloin emme voi tehdä sitä } }

Esimerkki yksiulotteisesta dynaamisesta vektorista, joka lajittelee ja poistaa kaksoiskappaleita:

#sisällytä <vektori> #sisällytä <merkkijono> #include <algoritmi> // Std::sort-, std::unique-algoritmien käyttäminen int main () { std :: vektori < std :: merkkijono > v_str ; //Tyhjä vektori v_str v_str . push_back ( "zz" ); // {"zz"} v_str . push_back ( "aa" ); // {"zz", "aa"} v_str . push_back ( "bb" ); // {"zz", "aa", "bb"} v_str . push_back ( "aa" ); // {"zz", "aa", "bb", "aa"} v_str . push_back ( "xx" ); // {"zz", "aa", "bb", "aa", "xx"} v_str . push_back ( "dd" ); // {"zz", "aa", "bb", "aa", "xx", "dd"} v_str . push_back ( "xx" ); // {"zz", "aa", "bb", "aa", "xx", "dd", "xx"} //Lajittele kaikki vektorin elementit std :: sort ( v_str . begin (), v_str . end ()); //Vektorilajittelun tulos: {"aa", "aa", "bb", "dd", "xx", "xx", "zz"} // Poista kaksoiskappaleet v_str . erase ( std :: ainutlaatuinen ( v_str . alkaa (), v_str . end () ), v_str . end () ); //Tulos kaksoiskappaleiden poistamisesta: {"aa","bb","dd","xx","zz"} return 0 ; }

Edut ja haitat

  • Kuten kaikki dynaamisen taulukon toteutukset , vektori ei käytä ylimääräisiä tietorakenteita, tiedot sijaitsevat vierekkäin muistissa, minkä ansiosta ne ovat hyvin välimuistissa .
  • Vektori voi varata nopeasti tietyn tiedon tallentamiseen tarvittavan muistin. Tämä on erityisen hyödyllistä tallennettaessa tietoja listoihin, joiden pituus ei välttämättä ole tiedossa ennen kuin luettelo on luotu, ja poistaminen (paitsi ehkä lopussa) on harvoin tarpeen.
  • Kuten muutkin STL-säilöt, se voi sisältää primitiivisiä tietotyyppejä, monimutkaisia ​​tai käyttäjän määrittämiä.
  • Vektori sallii satunnaisen pääsyn ; eli vektorielementtiin voidaan viitata samalla tavalla kuin taulukkoelementtiin (indeksin perusteella). Linkitetyt luettelot ja joukot eivät toisaalta tue satunnaiskäyttöä ja osoitinaritmetiikkaa.
  • Elementin poistaminen vektorista tai jopa vektorin tyhjentäminen ei välttämättä vapauta kyseiseen elementtiin liittyvää muistia. Tämä johtuu siitä, että vektorin enimmäiskoko sen luomisesta lähtien on hyvä kokoarvio uudelle vektorille.
  • Vektorit eivät ole tehokkaita lisättäessä elementtejä muualle kuin loppuun. Tällaisella operaatiolla on O(n) (katso O-merkintä ) monimutkaisuus verrattuna O(1): een linkitetyille listoille . Elementin poistamisella mielivaltaisesta paikasta on myös O(n) monimutkaisuus (alkuun on siirrettävä kaikki poistettavan jälkeen sijaitsevat elementit, mikä pahimmassa tapauksessa antaa n-1 siirtoa). Tämän kompensoi pääsyn nopeus. Vektorin mielivaltaiseen elementtiin pääsyssä on O(1) monimutkaisuus verrattuna O(n):iin linkitetylle listalle ja O(log n):ään tasapainotetussa binäärihakupuussa .

Muistiinpanot

  1. ISO / IEC (2003). ISO/IEC 14882:2003(E): Ohjelmointikielet - C++ § 23.1 Säiliövaatimukset [lib.container.requirements] para. neljä
  2. Josuttis, Nicolai C++-standardikirjasto - opetusohjelma ja  viite . - Addison-Wesley , 1999.
  3. ISO / IEC (2003). ISO/IEC 14882:2003(E): Ohjelmointikielet - C++ § 23.2.5 Luokkavektori<bool> [lib.vector.bool] para. yksi
  4. vektori<bool>: Lisää ongelmia, parempia ratkaisuja (PDF) (elokuu 1999). Haettu 1. toukokuuta 2007. Arkistoitu alkuperäisestä 31. elokuuta 2012.
  5. Tekninen määritys vektorin<bool> käytöstä poistamiseksi (maaliskuu 2007). Haettu 1. toukokuuta 2007. Arkistoitu alkuperäisestä 31. elokuuta 2012.
  6. ISO / IEC (2003). ISO/IEC 14882:2003(E): Ohjelmointikielet - C++ § 23.2.5 Luokkavektori<bool> [lib.vector.bool] para. 2
  7. 96. Vektori<bool> ei ole kontti . Haettu 1. tammikuuta 2009. Arkistoitu alkuperäisestä 31. elokuuta 2012.

Linkit