Vektor (C++)

A vektor ( std::vector<T>) egy szabványos C++ általános programozási minta, amely dinamikus tömböt valósít meg .

A sablon vectora fejlécfájlban található <vector>. Mint minden szabványos alkatrész, ez is a std . Ez az interfész egy szabványos C tömb működését emulálja (például gyors véletlenszerű hozzáférést az elemekhez), valamint néhány további szolgáltatást, például egy vektor automatikus átméretezését elemek beszúrásakor vagy eltávolításakor.

Egy vektor minden elemének azonos típusúnak kell lennie. Például nem tárolhat char és int adatokat együtt ugyanabban a vektorpéldányban. Az osztály vector szabványos metóduskészlettel rendelkezik az elemek elérésére, az elemek hozzáadására és eltávolítására, valamint a tárolandó elemek számának lekérésére.

Inicializálás

Egy vektor inicializálható bármilyen másoláskonstruktorral rendelkező típussal, amely meghatározott operator=és megfelel a következő feltételeknek [1] :

Kifejezés visszatérési típus Állapot
t = u T& tegyenértékűu
T(t) tegyenértékűT(t)
T(u) uegyenértékűT(u)
&u const T* címet mutatjau
t.~T()

Itt T a típus, amellyel a vektor inicializálva van, t egy típusú változó T, u egy típusú változó (esetleg const) T.

Például:

vektor < int > myVector ; // Üres vektor int típusú elemekből vector < float > myVector ( 10 ); // A 10- es vektor lebegteti a vektort < char > myVector ( 5 , ' ' ); // 5 szóközből álló vektor osztály T { ... }; int n = 10 ; vektor < T > myVector ( n ); // 10 egyedi T típusú elem vektora

Elemek elérése

A vektor egyes elemei az alábbi táblázatban leírt műveletekkel érhetők el. C és C++ megegyezés szerint az első elem indexe 0, az utolsó elem pedig size() - 1[2] indexszel .

Kifejezés visszatérési típus Határellenőrzés?
v.at(i) T&vagy const T&az i elemre Kivétel dobása lehetségesout_of_range
v[i] T&vagy const T&az i elemre Meghatározatlan viselkedés mikori >= v.size()
v.front() T&vagy const T&az első elemhez Meghatározatlan viselkedés mikorv.empty() == true
v.back() T&vagy const T&az utolsó elemre Meghatározatlan viselkedés mikorv.empty() == true

Ahol v egy (esetleg const) típusú objektum vector<T>, és i a vektor szükséges elemének indexe.

Néhány módszer

Az osztály vector egy konténer. A C++ szabvány szerint minden tárolónak tartalmaznia kell a begin(), end(), size(), max_size(), empty(), és metódusokat swap().

Az alábbiakban az elérhető módszerek rövid listája található leírással és a bonyolultság jelzésével.

Módszer Leírás Bonyolultság
Konstruktorok vector::vector Az alapértelmezett konstruktor. Nem vesz fel argumentumokat, új vektorpéldányt hoz létre O(1)(állandó időben működik)
vector::vector(const vector& c) Az alapértelmezett másoláskonstruktor. Létrehoz egy másolatot a vektorrólc O(n)(a vektor méretével arányos lineáris időben hajtja végre c)
vector::vector(size_type n, const T& val = T()) Létrehoz egy vektort nobjektumokkal. Ha valdeklarálva van, akkor ezen objektumok mindegyike inicializálódik az értékével; ellenkező esetben az objektumok egy alapértelmezett típusú konstruktor értéket kapnak T. O(n)
vector::vector(input_iterator start, input_iterator end) startLétrehoz egy vektort a és közötti elemekbőlend O(n)
Pusztító vector::~vector Megsemmisíti a vektort és elemeit
Üzemeltetők vector::operator= Egy vektor értékét másolja a másikba. O(n)
vector::operator== Két vektor összehasonlítása O(n)
Hozzáférés
az elemekhez
vector::at Elem elérése határon túli ellenőrzéssel O(1)
vector::operator[] Hozzáférés egy adott elemhez O(1)
vector::front Az első elem elérése O(1)
vector::back Hozzáférés az utolsó elemhez O(1)
Iterátorok vector::begin Egy iterátort ad vissza a vektor első eleméhez O(1)
vector::end Egy iterátort ad vissza a vektor utolsó eleme után O(1)
vector::rbegin Visszatér reverse_iteratoraz aktuális vektor végére. O(1)
vector::rend Visszatér reverse_iteratora vektor elejére. O(1)
Vektormérettel
dolgozik
vector::empty Visszatér true, ha a vektor üres O(1)
vector::size A vektor elemeinek számát adja vissza O(1)
vector::max_size Egy vektor elemeinek maximális lehetséges számát adja eredményül O(1)
vector::reserve Beállítja a vektor elemeinek minimális lehetséges számát O(n)
vector::capacity A vektor által tartalmazható elemek számát adja vissza, mielőtt több helyet kellene lefoglalnia. O(1)
vector::shrink_to_fit Csökkenti a használt memória mennyiségét a fel nem használt memória felszabadításával ( C++11 ) O(1)
Módosítók vector::clear Eltávolítja a vektor összes elemét O(n)
vector::insert Elemek beszúrása vektorba Beszúrás a végén, feltéve, hogy a memória nem kerül átcsoportosításra - O(1), tetszőleges helyre -O(n)
vector::erase Eltávolítja a megadott vektorelemeket (egy vagy több) O(n)
vector::push_back Elem beszúrása a vektor végére O(1)
vector::pop_back A vektor utolsó elemének eltávolítása O(1)
vector::resize Megváltoztatja a vektor méretét a megadott értékkel O(n)
vector::swap Cserélje fel két vektor tartalmát O(1)
Egyéb módszerek vector::assign Adott értékeket vektorral társítja O(n), ha a kívánt vektorméret be van állítva, a O(n*log(n))memória újraelosztása során
vector::get_allocator A memória lefoglalásához használt lefoglalót adja vissza O(1)

Iterátorok

A fent leírt közvetlen elemelérési funkciókon kívül egy vektor elemei iterátorokkal is elérhetők .

Az iterátorokat általában párban használják, amelyek közül az egyik az aktuális iterációt, a második pedig a tároló végét jelzi. Az iterátorok szabványos módszerekkel, begin()például end(). A függvény begin()visszaad egy mutatót az első elemre, és egy mutatót ad vissza egy end() képzeletbeli nem létező elemre, amely az utolsó után következik.

A vektor a leginkább funkciókban gazdag iterátortípust, a RandomAccessIterator-t (random access iterator) használja, amely lehetővé teszi a konténer tetszőleges sorrendben történő bejárását, valamint a bejárás során a vektor tartalmának megváltoztatását. Ha azonban a vektor megváltozik, az iterátor érvénytelenné válhat.

Példa a vektorelemek összegének kiszámítására iterátorok segítségével:

#include <iostream> #include <vektor> #include <iterátor> névtér használata std ; int main () { vektor < int > a_vektor ; vektor < int >:: iterátor the_iterator ;     for ( int i = 0 ; i < 10 ; i ++ ) {         a_vektor . push_back ( i );     }     int összesen = 0 ;     the_iterator = the_vector . kezdődik ();     while ( the_iterator != the_vector . end ()) {       összesen += * the_iterator ++ ; }           cout << "summa= " << összesen << endl ;     return 0 ; } vektor < int > a_vektor ; vektor < int >:: iterátor the_iterator ; for ( int i = 0 ; i < 10 ; i ++ ) { a_vektor . push_back ( i ); } int összesen = 0 ; the_iterator = the_vector . kezdődik (); while ( the_iterator != the_vector . end ()) { összesen += * the_iterator ; /* Vegye figyelembe, hogy az elem az iterátor hivatkozásának megszüntetésével érhető el */ ++ the_iterator ; } cout << "Összesen=" << összesen << endl ;

Eredmény:
Összesen=45

Vektor hangerő és átméretezés

Egy tipikus vektor megvalósítás egy dinamikus tömbre mutató mutató. Egy vektor mérete az elemek tényleges száma, a mérete pedig az általa használt memória mennyisége.

Ha egy vektorba új elemek beillesztésekor a mérete nagyobb lesz, mint a térfogata, akkor a memória átcsoportosításra kerül. Általában ez azt eredményezi, hogy a vektor új tárolóterületet foglal le, áthelyezi az elemeket és felszabadítja a régi területeket az új memóriahelyre.

Mivel a folyamat során az elemek címe megváltozik, a vektorban lévő elemekre vonatkozó hivatkozások vagy iterátorok érvénytelenek lehetnek. Az érvénytelen hivatkozások használata meghatározatlan viselkedést eredményez. Példa:

#include <vektor> int main () { std :: vektor < int > v ( 1 ); // Hozzon létre egy vektort egy int elemmel, amelynek értéke 0 int & first = * v . kezdődik (); // Hivatkozás létrehozása az első elemre v . beszúr ( v . vége (), v . kapacitás (), 0 ); // Új elemek hozzáadása int i = first ; // Meghatározatlan viselkedés. Lehet, hogy a link nem érvényes }

A módszer reserve()a szükségtelen memória-újrafoglalások megelőzésére szolgál. Hívás után reserve(n)a vektor mérete garantáltan nem kisebb, mint n. Példa:

#include <vektor> int main () { std :: vektor < int > v ( 1 ); // Hozzon létre egy vektort, amely egyetlen int típusú elemből áll, amelynek értéke 0 v . tartalék ( 10 ); // Helyfoglalás int & first = * v . kezdődik (); // Hivatkozás létrehozása az első elemre v . beszúr ( v . vége (), 5 , 0 ); // Elemek hozzáadása a vektorhoz int i = first ; // OK, mivel nem történt memória újrafoglalás }

Egy vektor megtartja az elemeinek meghatározott sorrendjét, így amikor egy új elemet beszúrunk a vektor elejére vagy közepére, a következő elemek visszafelé mozognak hozzárendelési operátoruk és másoláskonstruktoruk szempontjából. Ezért a beszúrási pont utáni elemhivatkozások és iterátorok érvénytelenek. Példa:

#include <vektor> int main () { std :: vektor < int > v ( 2 ); // Két Int típusú elemből álló vektor létrehozása // Hivatkozások létrehozása mindkét elemre int & first = v . elöl (); int & last = v . vissza (); v . beszúr ( v . kezdődik () + 1 , 1 , 1 ); // Új elemek hozzáadása a vektor közepéhez int i = first ; // Nem definiált viselkedés, ha egy beszúrás újraelosztást okozott int j = last ; // Nem definiált viselkedés a C++ szabvány szerint, §23.2.4.3/1 }

vektor<bool> szakirány

A C++ Standard Library vektorsablon specializációt határoz meg a bool. A specializáció szerint a vektornak úgy kell csomagolnia az elemeket, hogy a típus minden eleme bооlcsak egy bit memóriát használjon [3] . Ezt sokan hibának nevezik [4] [5] , mert nem felel meg a C++ Standard Libraryvector<bool> tároló követelményeinek . Például a tárolónak igaznak kell lennie T típusúnak. Nem ez a helyzet c -vel , amely egy helyőrző , amely [6] -ra konvertálható . Különben is, nem ad hivatkozással. Megállapodás van a C++ szabványosítási bizottság és a könyvtárfejlesztő csapat között, miszerint elavulttá kell tenni, majd el kell távolítani a szabványos könyvtárból , és a funkcionalitás visszaáll, de más néven. [7]<T>::referencelvaluevector<bool>::referenceboolvector<bool>::iteratorbool&vector<bool>

Használat

A vektort használó C++ programoknak tartalmazniuk kell egy fejlécfájlt <vector>:

#include <vektor> // Ezt követően inicializálhatjuk az std :: vector < T > myVector változót ;

Itt T - a tárolóban tárolt adatok típusa és myVector - a használt változó. Tbármilyen adattípus lehet, beleértve a felhasználó által meghatározott adattípust is.

Tömbhelyettesítés

A C és C++ nyelvekben egy tömb  a memória összefüggő blokkjaiban lévő adatok. Ezután minden blokkhoz hozzárendelnek egy indexet, és az egyes blokkok tartalma az index ismeretében megtalálható. Egy tömb minden elemének azonos típusúnak kell lennie.

A vektor hasonló a dinamikus tömbhöz, de egy vektor átméretezheti magát. Ezenkívül nincs szükség a memória manuális felszabadítására.

Mivel egy vektor elemei egymás mellett vannak tárolva, a vektor első elemének címe tömbként (az első elemre mutató mutató) átadható a függvénynek. A következő példa bemutatja, hogyan lehet egy vektort használni a C szabványos könyvtári függvényekkel memcpyés printf:

#include <cstring> // memcpy #include <vektor> #include <cstdio> // printf int main () { névtér használata std ; const char arr [] = "1234567890" ; // Vektor létrehozása 11 '\0' vektorral < char > vec ( sizeof arr ); // 11 'char' típusú elem másolása egy vektormemcpy -be ( vec . data (), arr , sizeof arr ); // Kiírja a "1234567890" printf ( "%s" , vec . data ()); }

Vegye figyelembe, hogy a memcpyés a használata nem javasolt, a C++ Standard Libraryprintf biztonságosabb alternatívái mellett

Használati példa

A következő példa különböző technikákat mutat be vektorral és C++ szabványos könyvtári algoritmusokkal , mint például a keverés, a rendezés, a legnagyobb elem megkeresése és a törlés-eltávolítás idióma használatával a vektorból való törlés.

#include <iostream> #include <vektor> #include <algoritmus> // rendezés, max_element, random_shuffle, remove_if, alsó_korlát #include <functional> // nagyobb, bind2nd // A kényelem kedvéért használjuk. A valós programokban óvatosan használja a névteret std ; int main () { int arr [ 4 ] = { 1 , 2 , 3 , 4 }; // Vektor inicializálása tömbvektor segítségével < int > numbers ( arr , arr + 4 ) ; // Számok hozzáadása a számvektorhoz . push_back ( 5 ); számok . push_back ( 6 ); számok . push_back ( 7 ); számok . push_back ( 8 ); // Most a vektor így néz ki: {1, 2, 3, 4, 5, 6, 7, 8} // Az elemek véletlenszerű keverése random_shuffle ( számok . kezd (), számok . vége ()); // Szerezd meg a maximális elemet, komplexitás O(n) vektor < int >:: const_iterator legnagyobb = max_elem ( számok . kezdete (), számok . vége () ); cout << "legnagyobb elem" << * legnagyobb << endl ; cout << "Ennek az elemnek az indexe" << legnagyobb - számok . kezdődik () << endl ; // Elemek rendezése, összetettség O(n log n) rendezés ( számok . kezdete (), számok . vége ()); // Keresse meg az 5-ös szám helyzetét a vektorban, összetettség O(log n) vektor < int >:: const_iterator five = alsó_korlát ( számok . kezd (), számok . vége (), 5 ); cout << "Az 5-ös szám az indexnél van" << öt szám . kezdődik () << endl ; // Minden 4 számnál nagyobb elem eltávolítása . erase ( remove_if ( számok . kezdete (), számok . vége (), bind2nd ( nagyobb < int > (), 4 )), számok . vége () ); // A többi kinyomtatása for ( vektor < int >:: const_iterator it = számok . kezdődik (); it != számok . vége (); ++ it ) { cout << * it << ' ' ; } return 0 ; }

A kimenet a következőket tartalmazza majd:

Legnagyobb elem 8

Ennek az elemnek az indexe 6 (megvalósításfüggő)

Az 5-ös szám a 4-es index alatt található

1 2 3 4

Példa egy 2-dimenziós dinamikus vektorra, valamint egy példa a hozzáférésére és módosítására

typedef std :: vektor < std :: vektor < int > > pxMP ; void function () { int X méret , Y méret ; // menet közben adja meg a méretet. pxMP pxMap ( sizeX , std :: vektor < int > ( sizeY )); // X/Y pixel méretű tömb 0.1. pxMap [ 0 ][ 5 ] = 1 ; /* hozzáférés */ // távolítsa el a pxMap bal és jobb oszlopát . pop_back (); pxMap . törlés ( pxMap.begin ( ) ); // távolítsd el az összes oszlopból a felső és alsó sort, először készíts ehhez néhány eszközt: std :: vector < std :: vector < int > >:: iterator iterlvl2 ; // iterátor a második dimenzióhoz. std :: vektor < int >:: iterator iterlvl1 ; // iterátor az első dimenzióhoz // Menjen mélyre a következőhöz: ( iterlvl2 = pxMap . begin (); iterlvl2 != pxMap . end (); iterlvl2 ++ ) { iterlvl1 = ( * iterlvl2 ). kezdődik (); // Csak bemutató célból ( * iterlvl2 ). pop_back (); ( * iterlvl2 ). törlés (( * iterlvl2 ) .begin ()); // Hol vagyunk? sizeY = ( * iterlvl2 ). méret (); // Állítsa be az Y méretet, amíg ezen a szinten vagyunk. Akkor nem fogjuk tudni megtenni } }

Példa egy egydimenziós dinamikus vektorra, a másolatok rendezésére és eltávolítására:

#include <vektor> #include <karakterlánc> #include <algoritmus> // Std::sort, std::unique algoritmusok használata int main () { std :: vektor < std :: string > v_str ; //Üres vektor 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"} //A vektor összes elemének rendezése std :: sort ( v_str . begin (), v_st.vége ( ) ); //A vektorrendezés eredménye: {"aa", "aa", "bb", "dd", "xx", "xx", "zz"} // Duplikátumok eltávolítása v_str . törlés ( std :: egyedi ( v_str . begin (), v_str . end () ), v_str . end () ); //Az ismétlődések eltávolításának eredménye: {"aa","bb","dd","xx","zz"} return 0 ; }

Előnyök és hátrányok

  • Mint a dinamikus tömb minden implementációja , a vektor sem használ további adatstruktúrákat, az adatok egymás mellett helyezkednek el a memóriában, aminek köszönhetően jól gyorsítótárazottak .
  • A vektor gyorsan lefoglalhatja a meghatározott adatok tárolásához szükséges memóriát. Ez különösen akkor hasznos, ha olyan listákban tárolja az adatokat, amelyek hossza a lista létrehozásáig nem ismert, és eltávolításra (talán a végét kivéve) ritkán van szükség.
  • Más STL-tárolókhoz hasonlóan primitív, összetett vagy felhasználó által meghatározott adattípusokat is tartalmazhat.
  • A vektor véletlen hozzáférést tesz lehetővé ; vagyis egy vektorelemre ugyanúgy hivatkozhatunk, mint egy tömbelemre (index alapján). A linkelt listák és halmazok viszont nem támogatják a véletlen hozzáférést és a mutató aritmetikát.
  • Egy elem vektorból való eltávolítása, vagy akár a vektor törlése nem feltétlenül szabadítja fel az elemhez társított memóriát. Ennek az az oka, hogy egy vektor maximális mérete a létrehozása óta jó méretbecslés egy új vektor számára.
  • A vektorok nem hatékonyak az elemek beszúrására bárhová, csak a végére. Egy ilyen művelet O(n) bonyolultságú (lásd O-jelölés ) a kapcsolt listák O(1)-hez képest . Egy elem tetszőleges helyről történő eltávolítása is O(n) bonyolultságú (az eltávolított elem utáni összes elemet az elejére kell mozgatni, ami a legrosszabb esetben n-1 lépést ad). Ezt kompenzálja a hozzáférés sebessége. Egy vektor tetszőleges elemének elérése O(1) bonyolultságú, összehasonlítva az O(n) értékkel egy linkelt lista és O(log n) értékével egy kiegyensúlyozott bináris keresési fa esetében .

Jegyzetek

  1. ISO / IEC (2003). ISO/IEC 14882:2003(E): Programozási nyelvek - C++ § 23.1 Tárolókövetelmények [lib.container.requirements] bek. négy
  2. Josuttis, Nicolai C++ Standard Library – oktatóanyag és  referencia . - Addison-Wesley , 1999.
  3. ISO / IEC (2003). ISO/IEC 14882:2003(E): Programozási nyelvek - C++ § 23.2.5 Osztályvektor<bool> [lib.vector.bool], bek. egy
  4. vektor<bool>: Több probléma, jobb megoldás (PDF) (1999. augusztus). Letöltve: 2007. május 1. Az eredetiből archiválva : 2012. augusztus 31..
  5. Specifikáció a vektor<bool> megszüntetésére (2007. március). Letöltve: 2007. május 1. Az eredetiből archiválva : 2012. augusztus 31..
  6. ISO / IEC (2003). ISO/IEC 14882:2003(E): Programozási nyelvek - C++ § 23.2.5 Osztályvektor<bool> [lib.vector.bool], bek. 2
  7. 96. A vektor<bool> nem konténer . Letöltve: 2009. január 1. Az eredetiből archiválva : 2012. augusztus 31..

Linkek