Choć większości wektory kojarzą się z symbolami ze strzałkami z lekcji fizyki, wektory to tak na prawdę uprządkowane zbiory wartości (intuicyjnie - jednokolumnowe macierze), które posiadają swój własny zbiór działań (m.in. mnożenie wektorowe). Podobnie jest z wektorami w C++, ponieważ są to po prostu tablice (dynamiczne), które posiadają swój własny zestaw funkcji. A dodatkowo kilka interesujących własności, do których spróbuję was przekonać. Mam nadzieję, że po przeczytaniu tego artykułu zaczniecie stosować wektory zamiast tablic.
Deklaracja i proste użycie
Na początek deklaracja wektora:
Przy czym w ostrych nawiasach podajemy typ/klasę, a w zwykłych ilość elementów, choć jak zobaczymy później, nie ma ona większego znaczenia... Oczywiście przy zastosowaniu using namespace std możemy pominąć człon "std::". No i oczywiście musimy dołączyć bibliotekę:
Najpierw mamy deklarację wektora. Następnie inicjujemy maszynę losującą i wpisujemy do kolejnych 100 elementów wektora liczby od 0 do 200. W kolejnej instrukcji posługujemy się wbudowaną w STL (biblioteka
algorithm) instrukcją sortowania, która jako parametry przyjmuje iterator (coś jakby wskaźnik) do elementu, od którego ma zacząć sortowanie i drugi, do elementu na którym ma skończyć. Aby je podać zastosowaliśmy funkcje begin i end, które zwracają iteratory odpowiednio początku i końca wektora. Funkcja sort działa szybciej i jest prostsza w użyciu, niż funkcja qsort z C, działająca na zwykłych tablicach. Przy okazji zwracam uwagę, że nie licząc celów edukacyjnych, nie należy ponownie wynajdować koła - używaj wbudowanego sortowania, zamiast swojego, kiedy tylko możesz. Kod będzie krótszy, prostszy i najprawdopodobniej szybszy.
Funkcji wektora jest oczywiście
więcej niż w przykładzie, jednak szczerze mówiąc zbyt mało, aby usatysfakcjonować kogoś przywykłego do nowszych języków, mimo wszystko bije to na głowę to, co dostajemy ze zwykłymi tablicami w C++.
Poruszanie się po wektorze za pomocą iteratorów
Zauważmy, że w ostatnim przykładzie po elementach wektora poruszaliśmy się za pomocą indeksów w nawiasach kwadratowych, zupełnie tak samo, jak w przypadku tablic. Możemy jednak robić to także za pomocą iteratorów, a odbywa się to bardzo podobnie, jak przy poruszaniu się po tablicy za pomocą wskaźników. Oto prymitywny przykład:
#include <iostream>
#include <vector>
using namespace std;
int main()
{
vector<int> wektor(5);
vector<int>::iterator i;
for(i=wektor.begin(); i!=wektor.end(); i++)
*i=8;
for(i=wektor.begin(); i!=wektor.end(); i++)
cout << *i << " ";
return 0;
}
Osobiście metoda ta wydaje mi się niewygodna, a w konsekwencji mało praktyczna. Sam jakoś nigdy nie miałem potrzeby używać jej w praktyce. Powyższy kod zamieściłem na wypadek, gdyby ktoś się ze mną nie zgadzał, porusza on jednak tylko cząstkę tego zagadnienia, ponieważ nie ukrywam - nie chciało mi się w nie zagłębiać. Jego wynikiem jest oczywiście 5 liczb "8", analizę tego prostego kodu pozostawiam Wam.
Wektory, a dynamiczne tablice
Ponieważ wektory są z definicji tablicami dynamicznymi, to aby uzyskać tablicę o ilości elementów zależnej od użytkownika, nie musimy już używać czegoś takiego:
cin >> n;
int *tablica = new int [n];
...
delete tablica;
Zobaczmy kolejny przykład:
#include <iostream>
#include <vector>
using namespace std;
int main()
{
vector<int> v(3);
int i;
for(i=0; i<10; i++)
v[i] = i;
for(i=0; i<10; i++)
cout << v[i] << " ";
cout << "\n";
return 0;
}
Zwróć uwagę na to, że deklarujemy tylko 3 elementy, a dalej chcemy operować na 10. Jest to jawne wykroczenie poza zakres, połączone z niszczeniem danych z pamięci, która nie należy do naszego programu. A raczej tak by było, gdybyśmy użyli zwykłej tablicy. Wektor jednak jest na tyle sprytny, że zwiększa swoje rozmiary, gdy chcemy coś zrobić z polem, które nie istnieje. Innymi słowy wektor zawsze ma dokładnie tyle pól ile trzeba, ale nie mniej niż zadeklarowaliśmy. Dlatego stosując wektor w programie, w którym najpierw pobieramy od użytkownika rozmiar tablicy, a potem jej elementy, praktycznie nie obchodzą nas kwestie związane z pamięcią!
Wektorowe dynamiczne macierze
Dynamiczne macierze zbudowane z tablic buduje się i niszczy w ten (dość zagmatwany) sposób:
cin >> wier >> kol;
int **a = new int*[kol];
for(i=0; i<kol; i++)
a[i] = new int[wier];
...
for (i=0; i<kol; i++)
delete [] a[i];
delete [] a;
Jak tworzyć wektorowe dynamiczne macierze zademonstruje na prostym przykładzie poniżej. Użytkownik podaje wymiary macierzy, a program generuje macierz z kolejno ponumerowanymi polami.
#include <iostream>
#include <vector>
using namespace std;
int main()
{
typedef vector<int> wektor;
typedef vector<wektor> macierz;
int i, j, n, m;
macierz mac;
wektor wiersz;
cout << "Podaj liczbe kolumn: ";
cin >> n;
cout << "Podaj liczbe wierszy: ";
cin >> m;
for(i=0; i<m; i++)
{
for(j=0; j<n; j++)
wiersz.push_back(j+n*i+1);
mac.push_back(wiersz);
wiersz.clear();
}
for(i=0; i<m; i++)
{
for(j=0; j<n; j++)
cout << mac[i][j] << "\t";
cout << "\n";
}
return 0;
}
Pogrubiłem fragmenty związane z deklaracją. Jak widać deklarujemy dwa nowe typy, z czego pierwszy jest pomocniczy, a drugi to wektor wektorów, czyli oczywiście macierz. W dalszej części programu widzimy, że operowanie na takiej macierzy wymaga trochę przyzwyczajenia - musimy się posługiwać funkcją push_back (dodaje podaną wartość na koniec wektora) na wierszu, a potem dodać wiersz do macierzy. Odczytywanie macierzy odbywa się już za to tradycyjnie.
To by było na tyle, mam nadzieję, że zainteresowałem Was wektorami. Miłej zabawy!
Niniejszy artykuł jest dostępny na licencji Creative Commons Uznanie autorstwa - Użycie niekomercyjne - Na tych samych warunkach 2.5 Polska.
Wszystkie przedstawione tutaj opinie należą do ich autorów i twórca strony nie ponosi żadnej odpowiedzialności za ich treść.
W polu "Strona WWW" wpisywanie członu "http://" nie jest konieczne. Tagi (X)HTML wpisane w treści nie będą działać jako element strony, zamiast tego pojawią się w samym komentarzu. Komentarze obraźliwe, nie na temat lub niezgodne z prawem będą w miarę możliwości usuwane.