Контейнеры STL: vector

Тип элемента вектора может быть произвольным и сколь угодно сложным! Например, мы могли бы описать студенческую группу так:

#include <iostream>

#include <vector> // подключаем необходимую библиотеку

using namespace std;

 

struct student {

char fio[80];

short age;

// конструктор объекта

student(const char* fio, short age) : age(age) { // присваиваем полю age,значение параметра age

// в поле fio структуры копируем параметр fio, который указывается при создании объекта

strncpy_s(this->fio, fio, sizeof(this->fio));

}

// ...

};

 

// перегружаем оператор для форматирования вывода объектов

inline ostream& operator <<(ostream& out, const student& obj) {

return out << obj.fio << "\t: " << obj.age;

}

 

int main(void) {

setlocale(LC_ALL, "rus");

 

vector<student> group;

// метод push_back() добавляет новую запись в конец вектора

group.push_back(student("Иванов И.И.", 20));

group.push_back(student("Петров П.П.", 21));

group.push_back(student("Сидоров С.С.", 19));

 

for (int i = 0; i < group.size(); i++)

cout << group[i] << endl;

}

В этом примере, помимо прочего, мы применили указатель this и перегрузку операторов. Оставляю ссылки для вас, если кому-то необходимо освежить это в памяти.

Шаблонным классом вектора (и любого контейнера STL) может быть, в свою очередь, контейнер STL. Например vector< vector<int>> или vector< vector< vector<int> > > (не забудьте пробел между закрывающимися скобками ‘>‘ — это особенность синтаксического разборщика). Таким образом мы можем, например, создать класс треугольных матриц:

#include <vector>

#include <cstring>

#include <iostream>

using namespace std;

 

struct trimatrix : vector< vector<double> > { // треугольная матрица

trimatrix(int n) {                      // n - размер

while (n > 0) {

vector<double> row(n);

for (int i = 0; i < n; i++)

row[i] = n - i;

push_back(row);

n--;

}

}

};

 

inline ostream& operator <<(ostream& out, const trimatrix& obj) {

for (int i = 0; i < obj.size(); i++) {

for (int j = 0; j < obj[i].size(); j++)

cout << obj[i][j] << " ";

cout << endl;

}

return out;

}

 

int main(void) {

cout << trimatrix(4);

}

Следующим уровнем нашего углубления в технику векторов, и контейнеров STL вообще, будет понятие итератора. Итератор — центральное понятие для работы с контейнерами STL. Итератор — это некоторая абстракция, которая применяется для выполнения итерации (перебора) элементов в контейнере STL и предоставления доступа к отдельным элементам. Итератор p не является указателем, но, на первых порах, вы можете условно считать его как нечто подобное по виду: *pбудет обозначать значение данных под текущим итератором,p++ переводит итератор на следующий элемент контейнера, аp— (когда это допустимо) — на предыдущий элемент. Для разных типов контейнеров, соответствующие им итераторы могут относиться к одной из 5-ти категорий: входные, выходные, однонаправленные, двунаправленные и произвольного доступа. Итераторы векторов — это итераторы прямого доступа. Именно поэтому для векторов возможна операция индексации. Этих, достаточно поверхностных, знаний про итераторы нам достаточно для того, чтобы начать работать с ними.

Воспроизведём в терминах итераторов задачу нахождения всех простых чисел, не превосходящих N (решето Эратосфена), которую мы уже решали раньше в технике массивов C++:

#include <iostream>

#include <vector>

#include <string>

using namespace std;

 

int main(int argc, char **argv) {

int k, n;                     // верхняя граница

vector<bool> a(n = stoi(argv[1]) + 1, true);

a[0] = a[1] = false;

for (k = 2; k < n; k++)

for (vector<bool>::iterator j = a.begin() + k + k;

                     j < a.end(); j += k)

   *j = false;

const int line = 10;          // выводить чисел в строку

k = 0;

for (vector<bool>::iterator j = a.begin(); j < a.end(); j++)

if (*j) {

cout << j - a.begin() << "\t";

if (0 == ++k % line) cout << endl;

}

if (k % line != 0) cout << endl;

return 0;

}

Что сделать, чтобы программа работала в MVS?

Чтобы этот код нормально работал, необходимо задать Аргумент команды в меню MVS (Microsoft Visual Studio). Делается это так: до запуска программы нажмите Alt + F7. Появится окно «Страницы свойств» вашего проекта. Далее введите значение 300 в поле Аргументы команды и нажмите Применить. Вот картинка:

int main( int argc, char **argv ) — здесь argc — число параметров (начиная с имени самой программы), а argv — массив строк char[] каждого из параметров. argv[ 1 ] в строке 8 и есть «300«. При помощи функции stoi() строка преобразуется в число типа int. То есть, наш вектор aпервоначально будет состоять из 301-го элемента, каждому из которых присвоено значение true

Как легко видеть из описания vector<bool>::iterator, что итератор хранит в себе вид контейнера, к которому относится, и тип элементов этого контейнера. Это требует достаточно громоздкой записи с точным описанием типа итератора. Но последний стандарт C++11 ввёл понятие выводимости типа: если требуемый тип объекта выводится из контекста его использования, то тип объекта может быть объявлен описателем auto (выводимый тип). Тогда строка 16 показанного выше кода может быть записана так:

for( auto j = a.begin(); j < a.end(); j++ )

Наконец, для векторов (и для всех контейнеров, имеющихдвунаправленные итераторы, как упоминалось выше) могут быть определены реверсные итераторы, которые перемещаются не от начала контейнера к концу, а наоборот — с конца в начало. Такой итератор должен объявляться как совсем другой тип, например:

vector<bool>::reverse_iterator i;

Но и здесь мы можем положиться на выведение типов, как в следующем примере:

#include <iostream>

#include <vector>

 

using namespace std;

 

int main(void) {

float data[] = { 1, 2, 3, 4, 5, 6, 7, 8, 9 };

int n = sizeof(data) / sizeof(data[0]); // кол-во элементов

vector<float> array(data, data + n);    // вектор из массива

 

for (auto j = array.begin(); j < array.end(); j++)

cout << *j << (j + 1 == array.end() ? "\n" : " ");

 

for (auto j = array.rbegin(); j < array.rend(); j++)

cout << *j << (j + 1 == array.rend() ? "\n" : " ");

}