Iterators là một trong những khái niệm nền tảng nhất trong C++ STL. Chúng là những "con trỏ thông minh" cho phép bạn duyệt qua các phần tử của container mà không cần biết cấu trúc bên trong của container đó. Bài học này khám phá chi tiết về các loại iterator, cách chúng hoạt động, khi nào chúng bị vô hiệu, và cách sử dụng range-based loops để viết code sạch hơn.

1. Iterator là gì? (Generalized Pointers)

Iterator là một object hoạt động giống như con trỏ. Nó có thể:

  • Trỏ tới một phần tử trong container
  • Được dereference để lấy giá trị: *iter
  • Di chuyển tới phần tử tiếp theo: ++iter hoặc iter++
  • So sánh với iterators khác: iter1 == iter2

Hãy xem sự khác biệt giữa iterator và con trỏ thông thường:

iterators_vs_pointers.cpp
#include <iostream>
#include <vector>

int main() {
    // === Con trỏ thường (chỉ hoạt động với mảng thô) ===
    int arr[] = {10, 20, 30, 40, 50};
    int* ptr = arr;
    std::cout << "Pointer to arr[0]: " << *ptr << "\n";  // 10
    ++ptr;
    std::cout << "Pointer to arr[1]: " << *ptr << "\n";  // 20

    // === Iterator (hoạt động với mọi container) ===
    std::vector<int> vec = {10, 20, 30, 40, 50};
    std::vector<int>::iterator it = vec.begin();
    std::cout << "Iterator to vec[0]: " << *it << "\n";  // 10
    ++it;
    std::cout << "Iterator to vec[1]: " << *it << "\n";  // 20

    // === Iterator có thêm tính năng (ví dụ: kiểm tra end) ===
    if (it != vec.end()) {
        std::cout << "Iterator is valid\n";
    }

    // === Duyệt với iterator ===
    std::cout << "All vector elements: ";
    for (auto iter = vec.begin(); iter != vec.end(); ++iter) {
        std::cout << *iter << " ";
    }
    std::cout << "\n";

    // === Iterator ngược ===
    std::cout << "Reverse iteration: ";
    for (auto rit = vec.rbegin(); rit != vec.rend(); ++rit) {
        std::cout << *rit << " ";
    }
    std::cout << "\n";

    return 0;
}

Các hàm iterator cơ bản:

  • begin() - Iterator tới phần tử đầu tiên
  • end() - Iterator tới vị trí sau phần tử cuối cùng (một phần tử quá cuối)
  • rbegin() - Iterator ngược tới phần tử cuối
  • rend() - Iterator ngược tới vị trí trước phần tử đầu
  • cbegin(), cend() - Const iterator (C++11)

2. Iterator Categories (5 Loại Iterator)

C++ STL định nghĩa 5 loại iterator theo khả năng của chúng. Mỗi loại hỗ trợ các phép toán khác nhau:

1) Input Iterator (Chỉ đọc, chỉ tiến)

Chỉ có thể đọc phần tử, chỉ có thể tiến. Ví dụ: istream_iterator.

input_iterator.cpp
#include <iostream>
#include <sstream>
#include <iterator>

int main() {
    std::istringstream input("10 20 30 40 50");

    // istream_iterator là input iterator
    std::istream_iterator<int> iter(input);
    std::istream_iterator<int> end;

    std::cout << "Values from input stream: ";
    while (iter != end) {
        std::cout << *iter << " ";
        ++iter;  // Chỉ có thể tiến, không thể lùi
    }
    std::cout << "\n";

    return 0;
}

2) Output Iterator (Chỉ ghi, chỉ tiến)

Chỉ có thể ghi phần tử, chỉ có thể tiến. Ví dụ: ostream_iterator, back_inserter.

output_iterator.cpp
#include <iostream>
#include <vector>
#include <algorithm>
#include <iterator>

int main() {
    std::vector<int> source = {1, 2, 3, 4, 5};
    std::vector<int> dest;

    // back_inserter là output iterator
    // Thêm từng phần tử vào cuối vector
    std::copy(source.begin(), source.end(),
              std::back_inserter(dest));

    // Ghi sang output stream
    std::copy(dest.begin(), dest.end(),
              std::ostream_iterator<int>(std::cout, " "));
    std::cout << "\n";

    return 0;
}

3) Forward Iterator (Đọc/ghi, chỉ tiến)

Có thể đọc và ghi, chỉ có thể tiến. Ví dụ: forward_list iterator.

forward_iterator.cpp
#include <iostream>
#include <forward_list>

int main() {
    std::forward_list<int> flist = {10, 20, 30, 40, 50};

    // Forward iterator: đọc, ghi, chỉ tiến
    for (auto it = flist.begin(); it != flist.end(); ++it) {
        std::cout << *it << " ";
        // ++it;  OK - tiến
        // --it;  LỖI - không thể lùi trên forward_list
    }
    std::cout << "\n";

    return 0;
}

4) Bidirectional Iterator (Đọc/ghi, cả hai hướng)

Có thể đọc, ghi, tiến và lùi. Ví dụ: list, map, set iterators.

5) Random Access Iterator (Đọc/ghi, truy cập tự do)

Có thể làm tất cả, cộng với truy cập O(1) bất kỳ vị trí nào. Ví dụ: vector, deque, string iterators.

Bảng so sánh Iterator Categories:

Iterator Type Read Write Forward Backward Random Access Containers
Input istream_iterator
Output back_inserter, ostream_iterator
Forward forward_list
Bidirectional list, map, set
Random Access vector, deque, string, array

Nhận xét: Mỗi loại iterator cao hơn có thể làm được tất cả những gì loại iterator thấp hơn có thể làm.

3. Iterator Invalidation (Khi Iterator bị vô hiệu)

Một trong những "hố tử" phổ biến nhất trong C++ là sử dụng iterator sau khi nó bị vô hiệu. Điều này tùy thuộc vào loại container:

vector: Iterator bị vô hiệu khi reallocate bộ nhớ

vector_invalidation.cpp
#include <iostream>
#include <vector>

int main() {
    std::vector<int> vec = {1, 2, 3, 4, 5};

    auto it = vec.begin();
    std::cout << "Original iterator points to: " << *it << "\n";  // 1

    // push_back có thể gây reallocate!
    vec.push_back(6);

    // Nếu reallocate xảy ra, iterator bây giờ vô hiệu
    // Dereference iterator vô hiệu => Undefined Behavior!
    // std::cout << *it << "\n";  // ❌ DANGER!

    // ✓ Cách an toàn: Lấy iterator mới sau push_back
    vec.push_back(7);
    it = vec.begin();  // Reassign
    std::cout << "New iterator points to: " << *it << "\n";  // 1

    return 0;
}

list: Iterator chỉ vô hiệu khi erase element đó

list_invalidation.cpp
#include <iostream>
#include <list>

int main() {
    std::list<int> lst = {1, 2, 3, 4, 5};

    auto it = lst.begin();
    std::advance(it, 2);  // it -> 3

    // insert/push_back không làm vô hiệu iterator
    lst.push_back(6);
    std::cout << "After push_back: " << *it << "\n";  // 3 (vẫn hợp lệ!)

    // Nhưng erase element mà iterator chỉ tới WILL invalidate it
    auto to_erase = it;
    ++it;  // Tìm element tiếp theo
    lst.erase(to_erase);

    // Nếu không làm ++it trước, iterator sẽ vô hiệu
    std::cout << "After erase: " << *it << "\n";  // 4 (vẫn hợp lệ!)

    return 0;
}

Safe Erase Pattern:

// ❌ DANGER - iterator bị vô hiệu sau erase
for (auto it = vec.begin(); it != vec.end(); ++it) {
    if (*it == 3) {
        vec.erase(it);  // Iterator bây giờ vô hiệu!
    }
}

// ✓ SAFE - erase trả về iterator hợp lệ tiếp theo
for (auto it = vec.begin(); it != vec.end(); ) {
    if (*it == 3) {
        it = vec.erase(it);  // erase trả về iterator tiếp theo
    } else {
        ++it;
    }
}

// ✓ MODERN (C++20) - std::erase
std::erase(vec, 3);  // Xóa tất cả 3 (nếu hỗ trợ)
              

4. Range-based Loops (C++11+)

C++11 giới thiệu range-based for loops, loại bỏ nhu cầu quản lý iterator thủ công. Chúng tự động xử lý iterator:

range_based_loops.cpp
#include <iostream>
#include <vector>
#include <map>

int main() {
    std::vector<int> vec = {1, 2, 3, 4, 5};

    // ❌ Cũ: Iterator thủ công
    std::cout << "Manual iterator: ";
    for (auto it = vec.begin(); it != vec.end(); ++it) {
        std::cout << *it << " ";
    }
    std::cout << "\n";

    // ✓ Mới: Range-based loop
    std::cout << "Range-based loop: ";
    for (int val : vec) {
        std::cout << val << " ";
    }
    std::cout << "\n";

    // ✓ Thay đổi giá trị bằng reference
    for (int& val : vec) {
        val *= 2;  // Nhân đôi mỗi phần tử
    }
    std::cout << "After doubling: ";
    for (int val : vec) {
        std::cout << val << " ";
    }
    std::cout << "\n";

    // ✓ Duyệt map (C++17 Structured Bindings)
    std::map<std::string, int> dict = {{"apple", 5}, {"banana", 3}};
    for (const auto& [key, value] : dict) {
        std::cout << key << " -> " << value << "\n";
    }

    // ✓ Const duyệt (không sửa)
    for (const int& val : vec) {
        std::cout << val << " ";
    }
    std::cout << "\n";

    return 0;
}

Lợi ích của Range-based Loops:

  • Code sạch hơn, dễ đọc hơn
  • Ít bugs iterator hơn
  • Tự động hoạt động với mảy container STL
  • Hỗ trợ structured bindings (C++17) cho pairs/tuples

5. Advanced Iterator Techniques

std::advance() và std::distance()

Sử dụng std::advance() để di chuyển iterator, và std::distance() để tính khoảng cách giữa hai iterators:

advance_distance.cpp
#include <iostream>
#include <vector>
#include <list>
#include <iterator>

int main() {
    std::vector<int> vec = {10, 20, 30, 40, 50};

    // ===== std::advance() =====
    auto it = vec.begin();
    std::advance(it, 2);  // Di chuyển 2 vị trí
    std::cout << "After advance by 2: " << *it << "\n";  // 30

    // ===== std::distance() =====
    auto first = vec.begin();
    auto last = vec.end();
    int dist = std::distance(first, last);
    std::cout << "Distance from begin to end: " << dist << "\n";  // 5

    // ===== Cẩn thận với list =====
    std::list<int> lst = {10, 20, 30, 40, 50};
    auto lit = lst.begin();
    // Trên vector: O(1). Trên list: O(n)!
    std::advance(lit, 4);
    std::cout << "List element at 4: " << *lit << "\n";  // 50

    return 0;
}

Iterator Adapters: reverse_iterator & back_inserter

iterator_adapters.cpp
#include <iostream>
#include <vector>
#include <algorithm>
#include <iterator>

int main() {
    std::vector<int> vec = {1, 2, 3, 4, 5};

    // ===== reverse_iterator =====
    std::cout << "Reverse with reverse_iterator: ";
    for (auto it = vec.rbegin(); it != vec.rend(); ++it) {
        std::cout << *it << " ";
    }
    std::cout << "\n";

    // ===== back_inserter =====
    std::vector<int> dest;
    std::copy(vec.begin(), vec.end(), std::back_inserter(dest));
    std::cout << "Copied to dest: ";
    for (int x : dest) {
        std::cout << x << " ";
    }
    std::cout << "\n";

    // ===== front_inserter (list only) =====
    std::list<int> lst;
    std::copy(vec.begin(), vec.end(), std::front_inserter(lst));
    std::cout << "Inserted to list front: ";
    for (int x : lst) {
        std::cout << x << " ";
    }
    std::cout << "\n";

    // ===== inserter =====
    std::vector<int> vec2 = {100, 200};
    std::copy(vec.begin(), vec.begin() + 2,
              std::inserter(vec2, vec2.begin() + 1));
    std::cout << "After inserter: ";
    for (int x : vec2) {
        std::cout << x << " ";
    }
    std::cout << "\n";

    return 0;
}

Custom Iterators cho User-Defined Classes

Bạn có thể tạo iterator cho class riêng bằng cách overload các phép toán cần thiết:

custom_iterator.cpp
#include <iostream>
#include <vector>

class SimpleArray {
private:
    std::vector<int> data;

public:
    SimpleArray(const std::vector<int>& d) : data(d) {}

    // Iterator class
    class Iterator {
    private:
        std::vector<int>::iterator it;

    public:
        Iterator(std::vector<int>::iterator i) : it(i) {}

        // Dereference
        int& operator*() { return *it; }

        // Increment
        Iterator& operator++() { ++it; return *this; }
        Iterator operator++(int) {
            Iterator temp = *this; ++it; return temp;
        }

        // Comparison
        bool operator!=(const Iterator& other) const {
            return it != other.it;
        }
        bool operator==(const Iterator& other) const {
            return it == other.it;
        }
    };

    Iterator begin() { return Iterator(data.begin()); }
    Iterator end() { return Iterator(data.end()); }
};

int main() {
    SimpleArray arr({10, 20, 30, 40, 50});

    std::cout << "Custom iterator: ";
    for (int val : arr) {
        std::cout << val << " ";
    }
    std::cout << "\n";

    return 0;
}

6. Iterator Debugging & Safety

Common Iterator Bugs và cách tránh:

iterator_safety.cpp
#include <iostream>
#include <vector>

int main() {
    std::vector<int> vec = {1, 2, 3, 4, 5};

    // ❌ BUG 1: Sử dụng iterator sau erase
    auto it = vec.begin();
    std::advance(it, 2);  // it -> 3
    vec.erase(it);
    // ++it;  // ❌ Undefined behavior

    // ✓ FIX: Lấy iterator mới từ erase
    vec = {1, 2, 3, 4, 5};
    for (auto it = vec.begin(); it != vec.end(); ) {
        if (*it == 3) {
            it = vec.erase(it);  // ✓ erase trả về iterator tiếp theo
        } else {
            ++it;
        }
    }

    // ❌ BUG 2: Iterator từ container khác
    std::vector<int> vec1 = {1, 2, 3};
    std::vector<int> vec2 = {4, 5, 6};
    auto it1 = vec1.begin();
    // vec2.erase(it1);  // ❌ CRASH - iterator từ vec1!

    // ✓ FIX: Sử dụng iterator từ container đúng
    vec2.erase(vec2.begin());

    // ❌ BUG 3: Dereference out-of-range iterator
    vec = {1, 2, 3};
    auto it3 = vec.end();
    // std::cout << *it3;  // ❌ Undefined - end() không chỉ tới element

    // ✓ FIX: Kiểm tra trước khi dereference
    if (it3 != vec.begin()) {
        --it3;
        std::cout << "Last element: " << *it3 << "\n";
    }

    return 0;
}

7. Comprehensive Example: All Iterator Features

iterators_comprehensive.cpp
#include <iostream>
#include <vector>
#include <list>
#include <algorithm>
#include <iterator>

int main() {
    std::vector<int> numbers = {5, 2, 8, 1, 9, 3};

    std::cout << "=== Original Vector ===\n";
    for (int n : numbers) std::cout << n << " ";
    std::cout << "\n\n";

    // Sort
    std::sort(numbers.begin(), numbers.end());
    std::cout << "=== After Sorting ===\n";
    for (int n : numbers) std::cout << n << " ";
    std::cout << "\n\n";

    // Find
    auto found = std::find(numbers.begin(), numbers.end(), 5);
    if (found != numbers.end()) {
        std::cout << "Found 5 at position: "
                  << std::distance(numbers.begin(), found) << "\n\n";
    }

    // Transform (nhân đôi mỗi element)
    std::vector<int> doubled;
    std::transform(numbers.begin(), numbers.end(),
                   std::back_inserter(doubled),
                   [](int x) { return x * 2; });

    std::cout << "=== Doubled Elements ===\n";
    for (int n : doubled) std::cout << n << " ";
    std::cout << "\n\n";

    // Copy to list với reverse iterator
    std::list<int> lst;
    std::copy(numbers.rbegin(), numbers.rend(),
              std::back_inserter(lst));

    std::cout << "=== List (Reversed Copy) ===\n";
    for (int n : lst) std::cout << n << " ";
    std::cout << "\n";

    return 0;
}

8. Quiz: Iterator Invalidation

Câu hỏi: Cho đoạn code sau, khi nào iterator bị vô hiệu?

std::vector<int> vec = {1, 2, 3, 4, 5};
auto it = vec.begin();
std::advance(it, 2); // it -> 3
vec.insert(vec.begin() + 1, 99); // Insert 99 at position 1
// Hỏi: *it vẫn hợp lệ không? Nếu vẫn hợp lệ, nó chỉ tới phần tử nào?

Trả lời: Iterator bị vô hiệu! Khi insert vào vector, tất cả iterators (ngoại trừ begin()) có thể bị vô hiệu nếu reallocate xảy ra. Ngay cả khi không reallocate, iterators chỉ tới hoặc sau điểm insert cũng bị invalidate.

Cách tránh: Cấp lại iterator sau insert:
it = vec.begin();
std::advance(it, 3); // Vị trí mới của 3 sau insert

Tải mã nguồn mẫu bài học

Bạn có thể tải các file mã nguồn C++ mẫu hoàn chỉnh của bài học này để tiến hành thực hành trực tiếp trên máy tính cá nhân.

Tải iterators_demo.cpp

This programming guide is only available in Vietnamese. Switch to Vietnamese to read the full article.