C++: изучаем стандартный контейнер vector
Стандартная библиотека шаблонов STL (Standard Template Library)позволяет создавать списки, векторы, очереди, стеки и т.д. из элементов любых типов данных. Не для всех контейнеров есть готовые шаблоны, скажем, их нет для матрицы, произвольного дерева узлов и т.д. Но в таких случаях можно использовать одни шаблоны для создания других.
Класс vector
- самый простой и часто применяемый шаблон стандартной библиотеки STL. По сути, он призван заменить "неуправляемый" (точнее, управляемый программистом) динамический массив. Вектор удобнее, чем обычный динамический массив. Он не ускорит работу вашей программы и не предоставит каких-либо "секретных механизмов" управления оперативной памятью. Он просто возьмёт на себя заботу о своей размерности и предоставит методы для типовых операций с массивами.
Для работы с векторами подключается заголовочный файл vector
и стандартное пространство имен:
#include <vector> using namespace std;
Пространство имён можно, конечно, и не подключать, но тогда придётся везде писать std::vector
вместо vector
.
Для начала опишем пустой вектор:
vector <int> v;
Из описания видно, что мы имеем дело с шаблоном, то есть, вектор можно составлять из элементов любого простого или составного типа. Здесь мы использовали тип элементов int
.
Теперь посмотрим, как вектор управляет распределением памяти:
v.reserve(10);
Этим оператором память зарезервирована под 10 элементов, но размерность вектора, возвращаемая методом v.size()
, всё равно равна нулю.
v.resize(10);
Вот теперь размерность вектора действительно равна 10, и метод v.size()
вернёт это значение.
Все показанные три действия мы могли выполнить одним оператором:
vector <int> v(10);
Здесь произошёл вызов конструктора класса vector
, мы которому передали аргумент-размерность, равный 10.
С элементами вектора можно работать двумя основными способами:
- переопределенным оператором
[]
- корректность индекса тогда не контролируется, как и для обычных массивов C/C++; - методом
at(i)
, гдеi
- индекс нужного элемента, при этом, контролируется корректность индекса, и, если нужно, генерируется исключение.
Обработка исключений в простейшем случае делается так:
1. Подключить заголовочный файл
<exception>
;2. "Подозрительный" код, который может привести к ошибке, заключить в блок
try
("попытаться"):try { /* сюда */ }3. Обработку ошибочных ситуаций вынести в блок
catch
("перехватить"):catch (exception e) { /* сюда */ }В частности, системное сообщение об ошибке будет возвращаться методом
e.what()
.
Например, показанный ниже код не сгенерирует исключения (в новых сборках Visual Studio 2019 уже сгенерируется системное исключение):
try { v[11] = 11; //не вылетит, но и не покажет элемент, //[] не контролирует корректность индекса, как и нативный оператор } catch (exception e) { //Этого исключения мы никогда не увидим cout << "\nError 1: " << e.what() << endl; }
А вот этот - сгенерирует:
try { v.at(11) = 11; } catch (exception e) { cout << "\nError 2: " << e.what() << endl; }
Проверить, пуст ли вектор, можно методом empty
:
if (!c.empty()) { /* контейнер не пуст */ }
Как и для динамических массивов, для обработки векторов часто пишут функции. Так как вектор - это шаблонный класс, удобнее в функциях также применять шаблоны. Например, метод печати вектора в консоль может иметь вид:
template <typename T> void printv (vector <T> &v) { //функция для вывода вектора в консоль int n = v.size(); cout << endl << "Size=" << n << " Items="; for (int i = 0; i < n; i++) cout << v[i] << " "; }
Это сработает с любым скалярным типом данных, для которого компилятор смог переопределить оператор []
:
vector <int> v(10); printv(v); vector <double> f(10); printv(f);
Скопировать вектор можно как минимум двумя способами:
1. Вызвать конструктор, которому передать ранее определенный вектор:
vector <int> b(v);
2. Скопировать вектор поэлементно в цикле for
с помощью оператора []
или метода at
, думаю, Вы легко напишете такой код самостоятельно.
Векторы можно сравнивать переопределённым оператором ==
:
cout << endl << (v == b);
Для добавления элементов сущестует метод insert
, имеющий несколько перегрузок:
v.insert(v.end(), 10); //добавили элемент со значением 10 в конец вектора v.insert(v.begin(), -10); //добавили элемент со значением -10 в начало вектора
Здесь мы уже чуть забежали вперёд, потому что метод insert
, как и другие методы класса,
для перебора элементов контейнера требует определить итератор (аналог указателя для обычных массивов):
vector <int>::iterator it = v.begin(); //определил итератор и поставил на начало it += 2; //сдвинулся на 2 позиции вправо v.insert(it, 5); //и вставил туда элемент
Можно вставить значение в вектор и другим методом:
v.emplace(it, 7);
Аналогично обстоит дело с методом erase
для удаления элементов и другими методами класса.
Более того, многие имена методов применимы и к другим контейнерам STL, а не только к вектору.
Чаще всего для добавления элемента в конец вектора пишут так:
v.push_back(13); //добавить в конец контейнера элемент
Возможен и вполне традиционный ввод элементов вектора с клавиатуры:
int n = 0; cin.clear(); cout << "Size="; cin >> n; vector <int> c(n); cout << "Items="; for (int i = 0; i < c.size(); i++) cin >> c[i]; printv(c);
Изучив итераторы и встроенные алгоритмы, мы могли бы выполнить обмен данными с консолью и без "видимых" циклов:
#include <iterator> //... copy(c.begin(), c.end(), ostream_iterator<int>(cout, " ")); //вывели вектор в консоль с разделителем элементов - пробелом //... copy(istream_iterator<int>(cin), istream_iterator<int>(),back_inserter(c)); //ввод вектора с помощью итератора из консоли, завершение - комбинация клавиш Ctrl+D
Как и на обычные динамические структуры, на контейнеры можно создавать указатели:
int *ptr = c.data();
и затем "шагать" по массиву, увеличивая или уменьшая указатель, правда, в обычных приложениях это едва ли пригодится.
Можно также инициализировать контейнер "обычным" статическим массивом:
int d[3] = { 1,2,3 }; vector <int> v(begin(d), end(d)); printv(v);
- классический код, или просто
vector <int> v { 1,2,3 };
в компиляторах Studio 2015 и выше.
Ну а всё остальное Вы без проблем освоите сами, изучив STL детальней :)
30.04.2017, 14:49 [13577 просмотров]