В предыдущем уроке мы определили переменную типа vector<float>, как эквивалент массива, размер которого мы сможем произвольно изменять по ходу выполнения кода. Но это не означает (так же как и для классических векторов C++), что мы можем таким образом создавать только динамические массивы простейших встроенных типов. Тип элемента вектора может быть произвольным и сколь угодно сложным! Например, мы могли бы описать студенческую группу так:
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 30 31 32 | #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> > > (не забудьте пробел между закрывающимися скобками ‘>‘ — это особенность синтаксического разборщика). Таким образом мы можем, например, создать класс треугольных матриц:
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 <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++:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 | #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; } |
Как легко видеть из описания vector<bool>::iterator, что итератор хранит в себе вид контейнера, к которому относится, и тип элементов этого контейнера. Это требует достаточно громоздкой записи с точным описанием типа итератора. Но последний стандарт C++11 ввёл понятие выводимости типа: если требуемый тип объекта выводится из контекста его использования, то тип объекта может быть объявлен описателем auto (выводимый тип). Тогда строка 16 показанного выше кода может быть записана так:
1 | for( auto j = a.begin(); j < a.end(); j++ ) |
Наконец, для векторов (и для всех контейнеров, имеющих двунаправленные итераторы, как упоминалось выше) могут быть определены реверсные итераторы, которые перемещаются не от начала контейнера к концу, а наоборот — с конца в начало. Такой итератор должен объявляться как совсем другой тип, например:
1 | vector<bool>::reverse_iterator i; |
Но и здесь мы можем положиться на выведение типов, как в следующем примере:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | #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" : " "); } |
Все возникающие вопросы по уроку задавайте в комментариях.
Наконец то что современное по срр, так держать
Страсть – сострадание чудесное , но свирепая иллюзия вынуждает нас усомниться в то , что одной страсти для милого развода слишком . Решившись на такой существенный рывок , как помолвка , нужно постараться методологически оценить обоснованность своего ҏешения .
ztvozs