Зовнішній та внутрішній ітератор. Реалізація на C++
Перед вивченням даної теми рекомендовано ознайомитись з наступними темами:
- Загальні відомості. Способи реалізації. Структурна схема. Приклад на C++
- Паттерн Iterator. Особливості реалізації на C++ для поліморфного контейнера та поліморфного ітератора
Зміст
- 1. Особливості реалізації паттерну Iterator. Зовнішній та внутрішній ітератор
- 2. Реалізація зовнішнього ітератора
- 3. Реалізація внутрішнього ітератора. Приклад на C++
- Зв’язані теми
Пошук на інших ресурсах:
1. Особливості реалізації паттерну Iterator. Зовнішній та внутрішній ітератор
Управління ітератором може виконувати:
- клієнт, який використовує цей ітератор. У цьому випадку ітератор називається зовнішнім або активним. Для зовнішнього ітератора клієнт явно запитує в ітератора наступний елемент щоб рухатись далі по агрегату. Зовнішні ітератори мають більшу гнучкість ніж внутрішні;
- безпосередньо ітератор. Такий ітератор має назву внутрішній ітератор або пасивний ітератор. У цьому випадку клієнт не заморочується над викликом операцій обходу, а просто передає ітератору деяку операцію, яка виконується над кожним елементом агрегату. Однак, внутрішні ітератори вважаються простішими у використанні.
⇑
2. Реалізація зовнішнього ітератора
Класична схема паттерну Iterator з використанням зовнішнього ітератора зображена на рисунку 1.
Рисунок 1. Схема паттерну Iterator з реалізацією на мові C++
Більш детально про реалізацію зовнішнього ітератора на мові C++ можна прочитати тут.
⇑
3. Реалізація внутрішнього ітератора. Приклад на C++
Для того, щоб реалізувати внутрішній ітератор, потрібно ввести додатковий клас-оболонку, який буде реалізовувати зовнішній ітератор.
У нижченаведеному коді розглядається динамічний масив Aggregate, для якого реалізовано внутрішній ітератор. Розглядається спрощений варіант з одним контейнером (Aggregate) та одним ітератором (Iterator). Поліморфний контейнер та поліморфний ітератор не розглядаються.
Код містить визначення таких класів:
- Iterator – клас ітератора;
- Aggregate – клас агрегату, всередині якого реалізовано динамічний масив;
- AggregateProcess – клас, який забезпечує реалізацію внутрішнього ітератора. Це є базовий клас для підкласу MultItemBy2;
- MultItemBy2 – клас, що перевизначає операцію ProcessItem() базового класу AggregateProcess. Операція виводить на екран добуток елементу контейнера Aggregate на число 2.
#include <iostream> using namespace std; // Паттерн Iterator для одного агрегату та одного ітератора // Попереднє оголошення класу template <class T> class Aggregate; // Клас ітератора template <class T> class Iterator { private: const Aggregate<T>* aggregate; long current; public: // Конструктор, який отримує агрегатний об'єкт Iterator(const Aggregate<T>* _aggregate) : aggregate(_aggregate), current(0) { } // Перехід (перевід курсору) на початок списку virtual void First() { current = 0; } // Перехід (перевід курсору) на наступний елемент списку virtual void Next() { current++; } // Перевірка, чи досягнуто кінець списку, // поточна позиція курсору знаходиться за останнім елементом списку virtual bool IsDone() const { return current >= aggregate->Count(); } // Отримати елемент списку за поточною позицією курсору virtual T CurrentItem() const { if (!IsDone()) return aggregate->GetItem(current); else { // Тут помилка, можна згенерувати виключення або // виконати інші дії cout << "Error." << endl; return 0; } } }; // Клас агрегату template <class T> class Aggregate { private: // дані агрегату (список, масив, ...) T* data; long count; public: // Конструктор Aggregate(long _count) { if (_count < 0) { count = 0; data = nullptr; return; } try { data = new T[_count]; count = _count; for (int i = 0; i < count; i++) data[i] = (T)0; } catch (bad_alloc e) { cout << e.what() << endl; count = 0; data = nullptr; } } // Конструктор копіювання Aggregate(const Aggregate& obj) { // скопіювати дані з obj в поточний екземпляр count = obj.count; // виділити пам'ять для масиву в цілому try { data = new T[count]; for (int i = 0; i < count; i++) data[i] = obj.data[i]; } catch (bad_alloc e) { cout << e.what() << endl; count = 0; } } // Метод, що повертає ітератор Iterator<T>* CreateIterator() const { return new Iterator<T>(this); } // Інші методи класу ConcreteAggregate long Count() const { return count; } T GetItem(long index) const { if ((index >= 0) && (index < count)) return data[index]; return data[0]; } // Метод, що додає в кінець списку елемент void Append(T value) { T* data2 = data; data = new T[count + 1]; for (int i = 0; i < count; i++) data[i] = data2[i]; data[count++] = value; delete[] data2; } // Видалення елементу зі списку, // index = 0, 1, ..., count-1 void Remove(long index) { if ((index >= 0) && (index < count)) { // Видалити елемент зі списку T* data2 = data; data = new T[count - 1]; for (int i = 0; i < index; i++) data[i] = data2[i]; for (int i = index + 1; i < count; i++) data[i - 1] = data2[i]; count--; delete[] data2; } } // Деструктор ~Aggregate() { if (count > 0) delete[] data; } // Виведення вмісту агрегату void Print(string text) { cout << text << endl; for (int i = 0; i < count; i++) cout << data[i] << " "; cout << endl; } }; // Клас, що реалізує внутрішній ітератор. // Цей клас є базовим для підкласів, що будуть реалізовувати // конкретні операції. template <class T> class AggregateProcess { private: // Ітератор всередині класу Iterator<T> it; public: // Конструктор - створює ітератор у класі AggregateProcess(Aggregate<T>* ag) : it(ag) { } // Метод, що обробляє усі елементи bool ProcessItems() { bool res = false; // 1. Перейти на перший елемент в контейнері it.First(); // 2. Цикл перебору всіх елементів while (!it.IsDone()) { // 2.1. Обробити поточний елемент, на який вказує ітератор it res = ProcessItem(it.CurrentItem()); // 2.2. Якщо елемент не оброблено, то вийти з циклу if (!res) break; // 2.3. Перейти до наступного елементу it.Next(); } // 3. Повернути результат обробки return res; } protected: // Віртуальний метод, що обробляє один елемент. // У підкласах цей метод має бути замінений // конкретною реалізацію, що здійснює специфічну обробку. virtual bool ProcessItem(const T&) = 0; }; // Підклас, що визначає операцію обробки окремого елементу контейнера. // Цей підклас успадковує клас AggregateProcess для того // щоб перевизначити операцію ProcessItem(). template <class T> class MultItemBy2 : public AggregateProcess<T> { public: // Конструктор MultItemBy2(Aggregate<T>* ag) : AggregateProcess<T>(ag) { } protected: // Метод обробки одного елементу контейнеру bool ProcessItem(const T& item) { // Вивести елемент, помножений на 2 cout << item * 2 << " "; // Обробка пройшла успішно return true; } }; void main() { // 1. Створити агрегат Aggregate<double> ag(0); ag.Append(2.55); ag.Append(3.8); ag.Append(-1.557); ag.Append(7.32); // 2. Створити внутрішній ітератор, який // буде виводити на екран кожен елемент контейнера // помножений на 2 MultItemBy2<double> it(&ag); bool res = it.ProcessItems(); if (res == true) cout << endl << "Ok!" << endl; else cout << endl << "Error" << endl; }
⇑
Зв’язані теми
- Загальні відомості. Способи реалізації. Структурна схема. Приклад на C++
- Паттерн Iterator. Особливості реалізації на C++ для поліморфного контейнера та поліморфного ітератора
- Паттерн Iterator. Реалізація на C#
- Паттерн Iterator. Реалізація на Java з підтримкою поліморфного контейнера та поліморфного ітератора
⇑