Векторы в C++: для начинающих
Всем привет! До этого дня мы использовали чистые массивы. Чистые — это значит простые массивы, не имеющие у себя в багаже различных функций. В этом уроке мы пройдем нечистые массивы — векторы.
Быстрый переход по статье:
Что такое вектор (vector)
Вектор — это структура данных, которая уже является моделью динамического массива.
Давайте вспомним о том, что для создания динамического массива (вручную) нам нужно пользоваться конструктором new
и вдобавок указателями. Но в случае с векторами всего этого делать не нужно.
Вообще, по стандарту пользоваться динамическим массивом через конструктор new
— не есть правильно. Так как в компьютере могут происходить различные утечки памяти.
Как создать вектор (vector) в C++
Сначала для создания вектора нам понадобится подключить библиотеку — <vector>
, в ней хранится шаблон вектора.
1 |
#include <vector> |
Кстати, сейчас и в будущем мы будем использовать именно шаблон вектора. Например, очередь или стек, не созданные с помощью массива или вектора, тоже являются шаблонными.
Далее, чтобы объявить вектор, нужно пользоваться конструкцией ниже:
1 |
vector < тип данных > <имя вектора>; |
- Вначале пишем слово
vector
. - Далее в угольных скобках указываем тип, которым будем заполнять ячейки.
- И в самом конце указываем имя вектора.
Вот пример:
1 |
vector <string> ivector; |
В примере выше мы создали вектор строк.
Кстати, заполнить вектор можно еще при инициализации (другие способы мы пройдем позже — в методах вектора). Делается это также просто, как и в массивах. Вот так:
1 |
vector <int> ivector = {<элемент [0]>, <[1]>, <[2]>}; |
После имени вектора ставим знак равенства и скобки, в которых через пробел указываем значение элементов.
Такой способ инициализации можно использовать только в C++!
Так, чтобы заполнить вектор строками, нам нужно использовать кавычки — "строка"
.
1 |
... = {"C", "+", "+"}; |
Второй способ обратиться к ячейке
Мы знаем, что в векторе для обращения к ячейке используются индексы. Обычно мы их используем совместно с квадратными скобками []
.
Но в C++ есть еще один способ это сделать благодаря функции — at(). В скобках мы должны указать индекс той ячейки, к которой нужно обратиться.
Вот как она работает на практике:
1 2 3 4 5 |
vector <int> ivector = {1, 2, 3}; ivector.at(1) = 5; // изменили значение второго элемента cout << ivector.at(1); // вывели его на экран |
Давайте запустим эту программу:
Как указать количество ячеек для вектора
Указывать размер вектора можно по-разному. Можно это сделать еще при его инициализации, а можно хоть в самом конце программы. Вот, например, способ указать длину вектора на старте:
1 |
vector <int> vector_first(5); |
Так в круглых скобках ()
после имени вектора указываем первоначальную длину. А вот второй способ:
1 2 |
vector <int> vector_second; // создали вектор vector_second.reserve(5); // указали число ячеек |
Первая строчка нам уже знакома. А вот во второй присутствует незнакомое слово — reserve
, это функция, с помощью которой мы говорим компилятору, какое количество ячеек нам нужно использовать.
Вы можете задать логичный вопрос:»А в чем разница?». Давайте создадим два вектора и по-разному укажем их количество ячеек.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 |
#include <iostream> #include <vector> // подключили библиотеку using namespace std; int main() { setlocale(0, ""); vector <int> vector_first(3); // объявили // два vector <int> vector_second; // вектора vector_second.reserve(3); cout << "Значения первого вектора (с помощью скобок): "; for (int i = 0; i < 3; i++) { cout << vector_first[i] << " "; } cout << "Значения второго вектора (с помощью reserve): " << endl; for (int i = 0; i < 3; i++) { cout << vector_second[i] << " "; } system("pause"); return 0; } |
Запускаем программу:
Как видим, в первом случае мы вывели три нуля, а во втором: 17, 0, 0.
Все потому, что при использовании первого способа все ячейки автоматически заполнились нулями.
При объявлении чего-либо (массива, вектора, переменной и т.д) мы выделяем определенное количество ячеек памяти, в которых уже хранится ненужный для ПК мусор. В нашем случае этим мусором являются числа.
Поэтому, когда мы вывели второй вектор, в нем уже находились какие-то рандомные числа — 17, 0, 0. Обычно они намного больше. Можете кстати попробовать создать переменную и вывести ее значение.
Нужно помнить! При использовании второго способа есть некоторый плюс — по времени. Так как для первого способа компилятор тратит время, чтобы заполнить все ячейки нулями.
Как сравнить два вектора
Если в середине программы нам понадобиться сравнить два массива, мы, конечно, используем цикл for и поочередно проверим все элементы.
Вектор снова на шаг впереди! Чтобы нам сравнить два вектора, потребуется применить всего лишь оператор ветвления if.
1 2 3 4 5 6 |
if (vec_first == vec_second) { // сравнили! cout << "Они равны!"; } else { cout << "Они не равны"; } |
Конечно, компилятор все равно прогонит эти два вектора по циклу, проверяя ячейки. Но посмотрите на код ниже и скажите насколько программа стала меньше, разве это не хорошо?
Вот так бы выглядела программа выше, если бы мы не использовали знак равенства для векторов.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
bool flag = true; if (vec_first.size() == vec_second.size()) { for (int i = 0; i < vec_first.size(); i++) { if (vec_first[i] != vec_second[i]) { cout << "Они не равны!"; flag = false; break; // выходим из цикла } } } else { flag = false; cout << "Они не равны!"; } if (flag) { cout << "Они равны!"; } |
- Сначала мы создали булеву переменную
flag
равнуюtrue
. У нее задача такая:- Если в условии (строки 5 — 10) она станет равна
false
— то значит эти векторы не равны и условие (строки 14 — 16) не будет выполняться. - Если же она после цикла (строки 3 — 12) останется равна
true
— то в условии (строки 14 — 16) мы сообщим пользователю, что они равны.
- Если в условии (строки 5 — 10) она станет равна
- В условии (строка 3) проверяем размеры двух векторов на равенство.
- И если условие (строки 5 — 10) будет равно
true
— то мы сообщим пользователю, что эти два вектора не равны.
Как создать вектор векторов
Понятно, что вам может понадобиться записать числа в двумерный массив. Но зачем использовать массив, если можно оперировать векторами.
Сейчас вы узнаете, как создать вектор векторов или простым языком массив векторов.
1 |
vector < vector < тип данных > >; |
Как можно увидеть, нам пришлось только добавить слова vector
и еще его <тип>
.
А чтобы указать количества векторов в векторе нам потребуется — метод resize()
.
1 2 |
vector < vector <int> > vec; vec.resize(10); // десять векторов |
Но есть еще одни способ добавления векторов в вектор. Для этого способа мы будем использовать функцию push_back()
(читайте ниже, что она делает).
1 |
vec.push_back(vector <int>()); |
- В аргументах функции
push_back()
находится — имя контейнера который мы хотим добавить. В нашем случае —vector
. - А дальше идет тип контейнера —
<тип>
. - И все заканчивается отрывающей и закрывающей скобкой
()
.
Для двумерного вектора тоже можно указать значения еще при инициализации:
1 2 3 |
vector < vector <int> > ivector = {{1, 4, 7}, {2, 5, 8}, {3, 6, 9}}; |
{1, 4, 7}
— это значения элементов первого массива (первого слоя). Такие блоки значений {1, 4, 7}
должны разделяться запятыми.
Методы для векторов:
Сейчас мы разберем некоторые методы, которые часто используются вместе с векторами. Метод — это функция, которая относится к определенному STL контейнеру.
В нашем случае этим STL контейнером является вектор. Если вы дальше собираетесь оперировать векторами — лучше все перечисленные функции запомнить.
1) size() и empty()
Если нам требуется узнать длину вектора, понадобится функция — size()
. Эта функция практически всегда используется вместе с циклом for.
1 2 3 |
for (int i = 0; i < ivector.size(); i++) { // ... } |
Также, если нам требуется узнать пуст ли стек, мы можем использовать функцию — empty()
.
- При отсутствии в ячейках какого-либо значения это функция возвратит —
true
. - В противном случае результатом будет —
false
.
Вот пример с ее использованием:
1 2 3 |
if (ivector.empty()) { // ... } |
2) push_back() и pop_back()
Как мы сказали выше, у векторов имеются методы, которые помогают оптимизировать и улучшить жизнь программистов. Одни из самых используемых функций это — push_back()
и pop_back()
.
- С помощью функции
push_back()
мы можем добавить ячейку в конец вектора. - А функция
pop_back()
все делает наоборот — удаляет одну ячейку в конце вектора.
Использование функции
push_back()
без указания значения ячейки — невозможно. Так или иначе, придется это значение указать!
Давайте разберемся, как работают эти функции на практике:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 |
#include <iostream> #include <vector> using namespace std; int main() { setlocale(0, ""); vector <string> vec_string; vec_string.push_back("апельсин"); // добавляем vec_string.push_back("груша"); // четыре vec_string.push_back("яблока"); // элемента vec_string.push_back("вишня"); // в конец вектора for (int i = 0; i < vec_string.size(); i++) { cout << vec_string[i] << " "; } cout << endl; vec_string.pop_back(); // удаляем элемент for (int i = 0; i < vec_string.size(); i++) { cout << vec_string[i] << " "; } system("pause"); return 0; } |
Вот что будет при запуске:
Кстати, можно сделать вот так:
1 2 3 4 5 6 |
vec_string.push_back("апельсин"); vec_string.push_back("груша"); string val = vec_string.pop_back(); // присвоили значение удаляемой ячейке cout << val; |
Мы сохранили значение удаляемой ячейки в переменную — val
.
Давайте запустим эту программу:
3) insert()
Выше мы вам показали функцию push_back()
, но то же самое можно сделать, используя функцию — insert()
. Только с помощью нее еще можно добавлять элементы в начало вектора.
Эта функция имеет такую конструкцию:
1 |
<имя вектора>.insert(<итератор>, <значение>); |
<итератор>
— про него мы поговорим в другом уроке. Это тема которая требует подробного изучения. Сейчас вам нужно знать только о том, что функция:
begin()
— указывает на начала вектора.-
end()
— указывает на конец вектора.
<значение>
— сюда мы должны вписать значение добавляемой ячейки.
Если вы в
<итератор>
указали именно итератор, а не функцииbegin()
иend()
, то элемент будет добавлен именно после той ячейки, на которую указывает итератор.
Давайте посмотрим, как insert()
работает на примере:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 |
int main() { setlocale(0, ""); vector vec_string(2); vec_string[0] = 2; // заполнили две vec_string[1] = 3; // ячейки for (int i = 0; i < vec_string.size(); i++) { cout << vec_string[i] << " "; } cout << endl; vec_string.insert(vec_string.begin(), 1); // добавили элемент в начало for (int i = 0; i < vec_string.size(); i++) { cout << vec_string[i] << " "; } cout << endl; vec_string.insert(vec_string.end(), 4); // добавили элемент в конец for (int i = 0; i < vec_string.size(); i++) { cout << vec_string[i] << " "; } system("pause"); return 0; } |
При запуске программы, мы увидим это:
На иллюстрациях ниже показано, как vec_string изменялся в программе:
Вы должны знать! Если вы хотите, чтобы ваша программа работала как можно быстрее — вам нужно добавлять элементы именно в конец.
Так как при добавлении элемента в начало, с помощью той же функции insert()
, в векторе происходит смещение всех ячеек вправо. К тому же, смещение идет по одной ячейке, а это у нас линейный поиск, который работает, мягко сказать, не быстро. При этом, чем больше у нас будет вектор, тем медленнее будет происходить добавление элементов!
4) front() и back()
Для того, чтобы мы могли просматривать первую и последнюю ячейки у нас имеются функции: front()
и back()
.
1 2 |
int front_num = ivector.front(); int back_num = ivector.back(); |
Упражнение
Чтобы как следует запомнить этот материал, создайте вектор и поочередно примените все выше сказанные функции.
Тест на тему «Векторы». Проверь себя!
Если эта надпись не исчезает долгое время, попробуйте обновить страницу. Этот тест использует javascript. Пожалуйста, влкючите javascript в вашем браузере.
На этом все! Надеемся, вы узнали для себя что-то новое! Если хотите задать вопрос, то пишите в комментарии. Удачи!
Подскажите, пожалуйста, где можно скачать дистрибутив С++, поддерживающий векторы?
Здравствуйте. Любой компилятор C++ должен поддерживать STL (Standard Template Library), а STL в свою очередь содержит реализации векторов (vector).
Спасибо за ответ, Дмитрий! Раз у нас тема «Для начинающих», задам такой вопрос: я подключила библиотеку
#include
а компилятор заругался. Что я не так сделала? Раньше никогда с векторами не работала, может, какая-то тонкость есть, которой я не знаю? С++ у меня DOS-ский.
ХМ… Имя библиотеки vector.h почему-то не пропечаталось…
Как мне с помощью вектора из массива с разными элементами вывести все не равные между собой элементы, т.е. например есть массив a[7]={1,1,2,3,4,3,7}, а вывести должен a[3]={2,4,7}.
Не понимаю фразу «вектора из массива», уточните. Как я понял элементы находятся в одномерном массиве или векторе. Чтобы решить данную задач создадим массив
int score[100000] = {0}
(размер массива зависит от входных данных), далее пройдемся по всем элементам массиваa
увеличивая значение ячейки массиваscore[arr[i]]++
. Если значение какой нибудь ячейки больше 1, значит есть повторяющие элементы данного значения и не надо выводитьa[i]
.Также есть решение с использованием контейнера map.
В коде сравнения двух векторов через цикл (поэлементное сравнение), если размеры массивов разные, то переменная flag останется true, то есть вывод будет: «Они не равны!Они равны!» — вероятно, нужно для этого случая также присвоить flag значение false
Спасибо, исправил!
» int num = vec_string.pop_back(); // присвоили значение удаляемой ячейке»
Не понимаю каким образом мы можем целочисленной переменной присвоить значение удаляемого элемента вектора строки. Оно и не работает собственно. Очепятка?
Спасибо, исправили!
Подскажите, пожалуйста, как вывести в консоли «Process returned 0 (0x0) execution time : 0.010 s»
после каждого выполнения программы
Можете использовать какую нибудь библиотеку для измерения скорости программы. Какую среду разработки вы используете? В codeblocks все должно выводиться.
Подскажите пожалуйста, как создать вектор неизвестной длины, который вводят с клавиатуры?
Для этого вам нужно инициализировать вектор. Далее добавляете в векторе элементы с помощью функции push_back(): my_vec.push_back(elem)
thread horse1([&]() {horses_actions(&horses_status); });