Ітератори STL
Концепція ітераторів Концепцію типу ітератора формують три ключові положення: • ітератор забезпечує непрямий доступ до елементів послідовності; • після інкременту вказує на наступний елемент послідовності; • забезпечує перевірку на співпадіння (рівність) з іншим ітератором.
Особливості ітераторів STL • Відповідність ітераторного типу концепції контейнера : § у векторі – це традиційний вказівник на тип контейнерового елемента, тому операції з ітератором еквівалентні операціям з вказівниками § у списку – спеціальний дружній клас, функціональність якого базується на безпосередній роботі з вузлами списку • Уніфікованість контейнерних ітераторів з метою застосовності до різнотипових контейнерів загальних алгоритмів § наявність бібліотечних ітераторних класів, які разом з допоміжними функціями оголошені в просторі назв std, заголовковий файл § надання ітераторам спеціальних властивостей
Категорії ітераторів Ітератор виведення Ітератор введення Ітератор прямий Ітератор двонаправлений Ітератор довільного доступу
Теги стандартних ітераторів //xutility internal header struct output_iterator_tag { output_iterator_tag() {; } }; struct input_iterator_tag { input_iterator_tag() {; } }; struct forward_iterator_tag : public input_iterator_tag { forward_iterator_tag() {; } }; struct bidirectional_iterator_tag : public forward_iterator_tag { bidirectional_iterator_tag() {; } }; struct random_access_iterator_tag : public bidirectional_iterator_tag random_access_iterator_tag() {; } }; {
Уніфіковані властивості ітераторів template struct iterator// базовий тип для ітераторів { typedef T value_type; typedef Distance difference_type; typedef Pointer pointer; //тип, який повертає operator –>() typedef Reference reference; //тип, який повертає operator *() typedef Category iterator_category; };
Тип характеристик ітераторів template struct iterator_traits { typedef typename Iterator: : value_type; typedef typename Iterator: : difference_type; typedef typename Iterator: : pointer; typedef typename Iterator: : reference; typedef typename Iterator: : iterator_category; };
Використання характеристик template typename iterator_traits: : difference_type Count (In. Iter first, In. Iter last, const T& val){ typename iterator_traits : : difference_type res=0; while (first!=last) if (*first++ ==val) ++res; return res; }
Категорії ітераторів стандартних класів Операції Категорія, позначення Ітератор введення ( In) Ітератор виведення ( Out) Прямий ітератор ( For) Двонаправлений ітератор ( Bi) Ітератор довільного доступу ( Ran) =*p ++ *p= –> == Клас istream_iterator istreambuf_iterator != ostream_iterator ostreambuf_iterator inserter() front_inserter() back_inserter() ++ *p= =*p –> ++ == *p= =*p –> ++ – – != == *p= =*p –> ++ – – + = < > >= != == – = <= != list set і map і multiset multimap Звичайні вказівники С++ vector deque
Генерування ітераторів контейнерами • Для роботи з конкретним контейнером генерується в тісному взаємозв’язку з характерними для типу контейнера класами, напр. , розподілювача пам’яті, вузла списку і т. п. . • Кожен стандартний контейнер автоматично обслуговує функціонування ітераторів. Сукупна система класів, задіяних з цією метою, є достатньо складною, але важливо, що це в основному класи реалізації. • Простота оголошення ітератора, достатньо лише вказати стандартну назву ітераторного типу, вбудованого в його шаблон, і далі використовувати притаманні для цього типу методи. Наприклад, два подібні оголошення vector : : iterator iter 1; list : : iterator iter 2; приводять до утворення ітератора довільного доступу в першому випадку, і двонаправленого в другому.
Ітератори введення, приклад використання - Дозволяють отримати елемент контейнера (=*p) - Не дозволяють змінювати елемент контейнера template Input. Iterator find (Input. Iterator first, Input. Iterator last, const T& value) { while (first != last && *first != value) ++first; return first; } Використані операції ітератора введення: !=, *, ++, тому тип ітератора будь-якого контейнера сумісний з типом Input. Iterator
Як ітератори введення можуть використовуватись три види об'єктів: 1) звичайні вказівники: int data[100]; . . . int * where = find(data, data+10, 7); 2) ітератори контейнерів list a. List; … List: : iterator where = find(a. List. begin(), a. List. end(), 7); 3) ітератори потоків введення, що визначаються класом istream_iterator
class istream_iterator template , class Distance = ptrdiff_t> class istream_iterator : public iterator { friend bool operator==(const istream_iterator&x, const istream_iterator& y); protected: istream* stream; T value; bool end_marker; void read (); public: typedef T value_type; typedef char. T char_type; typedef traits_type; typedef istream_type; istream_iterator() : stream(&cin), end_marker(false) {} istream_iterator(istream& s) : stream(&s) { read(); } istream_iterator(const istream_iterator& x ) : stream(x. stream) , value(x. value) , end_marker(x. end_marker) { } const T& operator* () const { return value; } istream_iterator& operator++ (); istream_iterator operator++(int); };
class istream_iterator • Визначає ітератор для вхідного потоку istream (сіn чи файл) • Дозволяє використовувати вхідний потік як контейнер елементів. • При визначенні об”єкта цього класу потрібно вказати: 1) параметр шаблону - тип елементів, що є в потоці: istream_iterator p; 2) проініціалізувати конкретним потоком або конструктором за замовчуванням : istream_iterator p(cin), pend; p – ітератор на перший елемент потоку pend - ітератор на елемент, що слідує за останнім елементом потоку
без >> istream_iterator is(cin); int i 1=*is; //перший елемент в потоці ++is; int i 2=*is; //другий елемент в потоці vector v(10); vector: : iterator ins=v. begin(); ifstream ifs("data. txt"); istream_iterator is(ifs), eostr; remove_copy(is, eostr, ins, 4); //скопіювати у вектор // всі елементи з файлу крім тих, що = 4
Ітератори виведення, приклад використання - Дозволяють змінювати значення елементів контейнера (*p= ) - Не повертають значення елементів контейнера template Output. Iterator copy (In first, In last, Output. Iterator result) { while (first != last) *result++ = *first++; return result; } - Використані операції ітератора виведення: *, ++, - тип ітератора будь-якого неконстантного контейнера сумісний з типом Output. Iterator, - також вказівник - та об’єкт ostream_iterator
class ostream_iterator template > class ostream_iterator : public iterator { protected: ostream* stream; const char* str; // delimiter public: typedef T value_type; typedef char. T char_type; typedef traits_type; typedef ostream istream_type; ostream_iterator(ostream& s) : stream(&s), str(0){} ostream_iterator(ostream& s, const char* c) : stream(&s), str((char *)c){} ostream_iterator( const ostream_iterator& x ) : stream(x. stream) , str(x. str){} ostream_iterator& operator=(const T& value){ *stream << value; if (str) *stream << str; return *this; } ostream_iterator& operator*(){ return *this; } ostream_iterator& operator++(int){ return *this; } };
class ostream_iterator • Це шаблон ітератора виведення для форматованого виведення об’єктів заданого типу в потік ostream. • Шаблонний параметр визначає тип елементів, що знаходяться в потоці. Це будь-який тип, для якого перевизначено оператор >> ostream_iterator out(cout); • Захищені поля цього класу є вказівниками на стандартний потік ostream і символ-роздільник (будьякий рядок; за замовчуванням 0 - нема): ostream_iterator intout(cout, “: "); • Нема конструктора за замовчуванням • Запис у потік реалізує операція operator=(): out=“gggg”; intout=23;
begin(in), end; ostream_iterator out(cout, "n"); out=“Begin” copy(begin, end," src="https://present5.com/presentation/27526832_238194764/image-19.jpg" alt="без << ifstream in(“data. txt"); istream_iterator begin(in), end; ostream_iterator out(cout, "n"); out=“Begin” copy(begin, end," />
без << ifstream in(“data. txt"); istream_iterator begin(in), end; ostream_iterator out(cout, "n"); out=“Begin” copy(begin, end, out); out = “Еnd!"; - На консоль (потік cout) виведеться спочатку “Begin”, далі послідовність рядків - вміст файлу data. txt. Далі виведеться “End!”. - Розділюючим рядком є "n“.
Ітератори вставки • Для роботи з контейнерами, які мають методи вставки елементів, передбачено шаблони спеціальних ітераторів вставки: - insert_iterator - front_insert_iterator - back_insert_iterator • Використовуються в тих алгоритмах, де вимагається вставка елементів. • Додають нові елементи і виконують автоматичний розподіл пам’яті, гарантуючи, що вставлені елементи додадуться до контейнера
class insert_iterator template аргумент шаблону – тип контейнера class insert_iterator : public iterator { private: typename Container: : iterator iter; protected: Container* container; public: typedef Container container_type; typedef typename Container: : value_type; insert_iterator(Container& x, typename Container: : iterator i) : container(&x), iter(i) {} insert_iterator& operator=(const typename Container: : value_type& value){ iter=container->insert(iter, value); ++iter; return *this; } insert_iterator& operator*(){ return *this; } insert_iterator& operator++ (int){ return *this; } };
class insert_iterator • Конструктор отримує два параметри: - адресу конкретного контейнера - ітератор цього контейнера, який вказує на місце вставки. • Вставка виконується операцією присвоєння operator=(): - викликається метод insert() контейнера - інкрементується ітератор ++iter
Ітераторна функція inserter() Функція inserter( ) створює ітератор вставки, використовуючи свої аргументи: контейнер Cont та ітератор Where template inline insert_iterator inserter(Container& Cont, Iter Where) { // return insert_iterator return (std: : insert_iterator(Cont, Where)); }
Інші ітератори вставки та ітераторні функції • Спеціалізовані на конкретне місце вставки елемента в послідовність: клас front_insert_iterator, функція front_inserter( ) – на початок клас back_insert_iterator, функція back_inserter( ) - після останнього елемента • Конструктори класів отримують один аргумент - послідовність (vector, deque, list) і створюють ітератори, які при вставці викликають методи push_front ( ) чи push_back( ), відповідно.
Інші ітератори вставки та ітераторні функції template class back_insert_iterator : public iterator{. . . }; template class front_insert_iterator : public iterator{. . }; template inline back_insert_iterator back_inserter(Cont& ); template inline front_insert_iterator front_inserter(Cont& );
Ітератори вставки, приклади використання int arr[4] = {3, 4, 7, 8}; vector v(arr, arr+4); insert_iterator > ins(v, v. begin()+2); ins = 5; Конструктор настроює інсертор на третій елемент вектора ins = 6; операція присвоєння дає змогу вставляти нові елементи Результат v: 3, 4, 5, 6, 7, 8.
istream_iterator begin(cin), end; vector vs; //copy(begin, end, vs. begin()); // не можна вставляти // елементи в порожній контейнер copy(begin, end, back_inserter(vs)); copy(vs. begin(), vs. end(), ostream_iterator (cout, ", "));
Прямі ітератори • Об'єднують властивості ітераторів введення та виведення • Допускають як модифікацію, так і доступ до вмістимого контейнера template void replace (Forward. Iterator first, Forward. Iterator last, сonst T& old_value, const T& new_value) { while (first != last) { if (*first == old_value) *first = new_value; ++first; Алгоритм заміни всіх значень old_value на } значення new_value контейнера, } визначеного ітераторами first і last
Двонаправлений ітератор • Додатково підтримує операцію декременту (operator-) • Дає змогу переміщувати ітератор по контейнеру як в прямому, так і в зворотньому напрямку template Output. Iterator reverse_copy (Bidirectional. Iterator first, Bidirectional. Iterator last, Output. Iterator result) { while (first != last) Алгоритм зворотнього копіювання *result++ = *--last; послідовності елементів контейнера, визначеної ітераторами first і last return result; }