C++STL(九):迭代器

1.基本概念

#include 
#include 

int main() {
    std::vector<int> vec = {1, 2, 3, 4, 5};
    
    // 迭代器的基本使用
    std::vector<int>::iterator it = vec.begin();
    while (it != vec.end()) {
        std::cout << *it << " ";
        ++it;
    }
    
    // 迭代器与指针的相似性
    int* ptr = &vec[0];
    std::cout << *ptr << " " << *vec.begin() << std::endl;
    
    return 0;
}

2.分类

#include 
#include 
#include 
#include 
#include 

int main() {
    // 输入迭代器示例 -- 只读,单向,单遍扫描
    std::istream_iterator<int> input_it(std::cin);
    
    // 输出迭代器示例 -- 只写,单向,单遍扫描
    std::ostream_iterator<int> output_it(std::cout, " ");
    
    // 前向迭代器示例 -- 可读写,单向,多遍扫描
    std::forward_list<int> flist = {1, 2, 3};
    auto fwd_it = flist.begin();
    
    // 双向迭代器示例 -- 可读写,双向,多遍扫描
    std::list<int> lst = {1, 2, 3};
    auto bidir_it = lst.begin();
    ++bidir_it;
    --bidir_it;  // 可以双向移动
    
    // 随机访问迭代器示例 -- 可读写,随机访问
    std::vector<int> vec = {1, 2, 3, 4, 5};
    auto random_it = vec.begin();
    random_it += 2;  // 可以随机访问
    std::cout << random_it[1] << std::endl;  // 支持下标访问
    
    return 0;
}

3.特性与操作

#include 
#include 
#include 

int main() {
    std::vector<int> vec = {10, 20, 30, 40, 50};
    
    // 共同操作
    auto it = vec.begin();
    std::cout << *it << std::endl;  // 解引用
    it++;                           // 前进
    bool equal = (it == vec.begin()); // 比较
    
    // 随机访问迭代器操作
    auto random_it = vec.begin();
    random_it += 2;                 // 前进多步
    random_it -= 1;                 // 后退多步
    std::cout << random_it[2] << std::endl;  // 下标访问
    bool less = (random_it < vec.end());     // 比较大小
    
    // 迭代器失效示例
    std::vector<int> numbers = {1, 2, 3, 4, 5};
    for (auto it = numbers.begin(); it != numbers.end(); /* 不在这里递增 */) {
        if (*it % 2 == 0) {
            it = numbers.erase(it);  // erase返回下一个有效迭代器
        } else {
            ++it;
        }
    }
    
    return 0;
}

4.STL容器操作导致的迭代器失效情况

1.顺序容器

vector 和 string

  • 插入操作
    • 如果容器需要重新分配内存(容量改变),则所有迭代器和引用都会失效
    • 如果不需要重新分配,则插入点之前的迭代器和引用有效之后失效
  • 删除操作
    • 删除点之前的迭代器和引用仍有效
    • 删除点及之后的迭代器和引用都失效
std::vector<int> vec = {1, 2, 3, 4, 5};
auto it = vec.begin() + 2;  // 指向元素3

// 在开头插入元素,可能导致所有迭代器失效(如果发生重新分配!!)
vec.insert(vec.begin(), 0);  // it 现在可能已失效

// 安全做法
it = vec.begin() + 2;  // 重新获取迭代器

deque

  • 插入操作
    • 在两端插入:只有指向容器的迭代器失效,指向元素的引用和指针不受影响
    • 在中间插入:所有迭代器、引用和指针都失效
  • 删除操作
    • 在两端删除:只有指向被删除元素的迭代器失效
    • 在中间删除:所有迭代器、引用和指针都失效
std::deque<int> dq = {1, 2, 3, 4, 5};
auto it = dq.begin() + 2;  // 指向元素3

// 在中间插入元素,所有迭代器失效
dq.insert(dq.begin() + 1, 10);  // it 现在已失效

// 安全做法
it = dq.begin() + 2;  // 重新获取迭代器

list 和 forward_list

  • 插入操作
    • 所有指向容器的迭代器(包括尾后迭代器)仍有效
    • 指向元素的引用和指针仍有效
  • 删除操作
    • 只有指向被删除元素的迭代器、引用和指针失效
    • 其他迭代器、引用和指针仍有效
std::list<int> lst = {1, 2, 3, 4, 5};
auto it1 = lst.begin();  // 指向元素1
auto it2 = std::next(it1, 2);  // 指向元素3

// 删除元素1,只有it1失效
lst.erase(it1);  // it1失效,it2仍有效

// 安全做法
it1 = lst.begin();  // 重新获取迭代器

2. 关联容器

set, map, multiset, multimap

  • 插入操作
    • 所有迭代器和引用仍有效
    • 指针仍有效
  • 删除操作
    • 只有指向被删除元素的迭代器失效
    • 其他迭代器、引用和指针仍有效
std::set<int> s = {1, 2, 3, 4, 5};
auto it1 = s.find(1);
auto it2 = s.find(3);

// 删除元素1,只有it1失效
s.erase(it1);  // it1失效,it2仍有效

// 安全做法
it1 = s.find(2);  // 获取新的迭代器

3. 无序容器

unordered_set, unordered_map, unordered_multiset, unordered_multimap

  • 插入操作
    • 如果插入操作导致哈希表重新哈希(rehash),则所有迭代器失效,但引用和指针仍有效
    • 如果没有rehash,则所有迭代器、引用和指针仍有效
  • 删除操作
    • 只有指向被删除元素的迭代器失效
    • 其他迭代器、引用和指针仍有效
std::unordered_set<int> us = {1, 2, 3, 4, 5};
auto it = us.find(3);

// 插入大量元素可能导致rehash
for (int i = 10; i < 100; ++i) {
    us.insert(i);  // 如果发生rehash,it将失效
}

// 安全做法
it = us.find(3);  // 重新获取迭代器

4. 安全处理迭代器失效的通用策略

// 1. 保存操作返回的迭代器
template <typename Container>
void safe_erase1(Container& c, typename Container::iterator it) {
    it = c.erase(it);  // erase返回下一个有效迭代器
    // 继续使用it...
}

// 2. 预先递增迭代器
template <typename Container>
void safe_erase2(Container& c, typename Container::iterator& it) {
    auto next_it = std::next(it);
    c.erase(it);
    it = next_it;  // 使用预先保存的下一个迭代器
}

// 3. 使用索引而非迭代器(适用于支持随机访问的容器)
void safe_operation_with_index(std::vector<int>& vec) {
    for (size_t i = 0; i < vec.size(); /* 不在这里递增 */) {
        if (vec[i] % 2 == 0) {
            vec.erase(vec.begin() + i);  // 不需要更新i,因为元素已经移动
        } else {
            ++i;
        }
    }
}

5.迭代器适配器

#include 
#include 
#include 
#include 

int main() {
    std::vector<int> vec;
    
    // 插入迭代器
    std::fill_n(std::back_inserter(vec), 5, 10);  // 在尾部插入5个10
    
    std::vector<int> vec2 = {1, 2, 3};
    std::copy(vec2.begin(), vec2.end(), std::back_inserter(vec));
    
    // 流迭代器
    std::copy(vec.begin(), vec.end(), 
              std::ostream_iterator<int>(std::cout, " "));
    std::cout << std::endl;
    
    // 反向迭代器
    std::vector<int> vec3 = {1, 2, 3, 4, 5};
    std::copy(vec3.rbegin(), vec3.rend(), 
              std::ostream_iterator<int>(std::cout, " "));  // 输出:5 4 3 2 1
    
    return 0;
}

6.迭代器萃取技术

迭代器萃取是一种类型特性(type traits)技术,主要用于在编译期获取迭代器的相关类型信息,如:

  • 迭代器所指向的值类型(value_type)
  • 迭代器之间的距离类型(difference_type)
  • 迭代器的类别(iterator_category)
  • 指针类型(pointer)
  • 引用类型(reference)
#include 
#include 
#include 
#include 
#include 

// 迭代器萃取的基本使用
template <typename Iterator>
void printIteratorInfo() {
    // 使用iterator_traits获取迭代器的相关类型信息
    using Traits = std::iterator_traits<Iterator>;
    using ValueType = typename Traits::value_type;
    using DiffType = typename Traits::difference_type;
    using Category = typename Traits::iterator_category;
    
    std::cout << "值类型: " << typeid(ValueType).name() << std::endl;
    std::cout << "距离类型: " << typeid(DiffType).name() << std::endl;
    
    // 根据迭代器类别输出不同信息
    if (std::is_same<Category, std::random_access_iterator_tag>::value) {
        std::cout << "迭代器类别: 随机访问迭代器" << std::endl;
    } else if (std::is_same<Category, std::bidirectional_iterator_tag>::value) {
        std::cout << "迭代器类别: 双向迭代器" << std::endl;
    } else if (std::is_same<Category, std::forward_iterator_tag>::value) {
        std::cout << "迭代器类别: 前向迭代器" << std::endl;
    } else if (std::is_same<Category, std::input_iterator_tag>::value) {
        std::cout << "迭代器类别: 输入迭代器" << std::endl;
    } else if (std::is_same<Category, std::output_iterator_tag>::value) {
        std::cout << "迭代器类别: 输出迭代器" << std::endl;
    }
}

// 根据迭代器类别优化的advance实现
template <typename Iterator, typename Distance>
void optimizedAdvance(Iterator& it, Distance n, std::random_access_iterator_tag) {
    // 随机访问迭代器可以直接加法
    std::cout << "使用随机访问迭代器优化" << std::endl;
    it += n;
}

template <typename Iterator, typename Distance>
void optimizedAdvance(Iterator& it, Distance n, std::bidirectional_iterator_tag) {
    // 双向迭代器需要根据n的正负选择前进或后退
    std::cout << "使用双向迭代器实现" << std::endl;
    if (n >= 0) {
        while (n--) ++it;
    } else {
        while (n++) --it;
    }
}

template <typename Iterator, typename Distance>
void optimizedAdvance(Iterator& it, Distance n, std::input_iterator_tag) {
    // 输入迭代器只能向前移动
    std::cout << "使用输入迭代器实现" << std::endl;
    while (n--) ++it;
}

// 统一的接口
template <typename Iterator, typename Distance>
void myAdvance(Iterator& it, Distance n) {
    // 使用iterator_traits获取迭代器类别,并调用相应的实现
    optimizedAdvance(it, n, typename std::iterator_traits<Iterator>::iterator_category());
}

// 自定义迭代器示例
template <typename T>
class MyIterator {
public:
    // 迭代器必须定义这些类型,供iterator_traits使用
    using iterator_category = std::forward_iterator_tag;
    using value_type = T;
    using difference_type = std::ptrdiff_t;
    using pointer = T*;
    using reference = T&;
    
    // 迭代器实现...
};

int main() {
    // 测试不同容器的迭代器
    std::vector<int> vec = {1, 2, 3, 4, 5};
    std::list<double> lst = {1.1, 2.2, 3.3};
    
    std::cout << "Vector迭代器信息:" << std::endl;
    printIteratorInfo<std::vector<int>::iterator>();
    
    std::cout << "\nList迭代器信息:" << std::endl;
    printIteratorInfo<std::list<double>::iterator>();
    
    std::cout << "\n指针作为迭代器的信息:" << std::endl;
    printIteratorInfo<int*>();
    
    // 测试优化的advance
    auto vecIt = vec.begin();
    auto lstIt = lst.begin();
    
    std::cout << "\n对vector迭代器执行advance:" << std::endl;
    myAdvance(vecIt, 2);
    std::cout << "结果: " << *vecIt << std::endl;  // 应该是3
    
    std::cout << "\n对list迭代器执行advance:" << std::endl;
    myAdvance(lstIt, 2);
    std::cout << "结果: " << *lstIt << std::endl;  // 应该是3.3
    
    return 0;
}
}

7.自定义迭代器

#include 
#include 

// 简单的整数范围迭代器
class IntRangeIterator {
private:
    int value;
public:
    // 迭代器特性定义
    using iterator_category = std::forward_iterator_tag;
    using value_type = int;
    using difference_type = std::ptrdiff_t;
    using pointer = int*;
    using reference = int&;
    
    IntRangeIterator(int val) : value(val) {}
    
    // 解引用操作
    int operator*() const { return value; }
    
    // 前缀递增
    IntRangeIterator& operator++() {
        ++value;
        return *this;
    }
    
    // 后缀递增
    IntRangeIterator operator++(int) {
        IntRangeIterator temp = *this;
        ++(*this);
        return temp;
    }
    
    // 相等比较
    bool operator==(const IntRangeIterator& other) const {
        return value == other.value;
    }
    
    // 不等比较
    bool operator!=(const IntRangeIterator& other) const {
        return !(*this == other);
    }
};

// 整数范围容器
class IntRange {
private:
    int begin_value;
    int end_value;
public:
    IntRange(int begin, int end) : begin_value(begin), end_value(end) {}
    
    IntRangeIterator begin() const { return IntRangeIterator(begin_value); }
    IntRangeIterator end() const { return IntRangeIterator(end_value); }
};

int main() {
    IntRange range(1, 6);
    for (int value : range) {
        std::cout << value << " ";  // 输出:1 2 3 4 5
    }
    std::cout << std::endl;
    
    return 0;
}

8.与算法配合

#include 
#include 
#include 
#include 
#include 

int main() {
    // 不同容器的迭代器与算法配合
    std::vector<int> vec = {5, 2, 8, 1, 9};
    std::list<int> lst = {3, 7, 4, 6, 10};
    
    // 排序算法 - 需要随机访问迭代器
    std::sort(vec.begin(), vec.end());  // 可以,vector提供随机访问迭代器
    // std::sort(lst.begin(), lst.end());  // 错误,list只提供双向迭代器
    lst.sort();  // 使用list的成员函数排序
    
    // 查找算法 - 只需要输入迭代器
    auto it1 = std::find(vec.begin(), vec.end(), 8);
    auto it2 = std::find(lst.begin(), lst.end(), 7);
    
    // 使用迭代器适配器与算法
    std::vector<int> result;
    std::merge(vec.begin(), vec.end(), 
               lst.begin(), lst.end(), 
               std::back_inserter(result));
    
    std::copy(result.begin(), result.end(), 
              std::ostream_iterator<int>(std::cout, " "));
    
    return 0;
}

9.最佳使用场景

#include 
#include 
#include 

int main() {
    std::vector<int> vec = {1, 2, 3, 4, 5};
    
    // 1. 使用auto简化迭代器声明
    for (auto it = vec.begin(); it != vec.end(); ++it) {
        std::cout << *it << " ";
    }
    std::cout << std::endl;
    
    // 2. 使用范围for循环简化迭代
    for (const auto& elem : vec) {
        std::cout << elem << " ";
    }
    std::cout << std::endl;
    
    // 3. 安全处理迭代器失效
    auto it = vec.begin();
    while (it != vec.end()) {
        if (*it % 2 == 0) {
            it = vec.erase(it);  // 返回下一个有效迭代器
        } else {
            ++it;
        }
    }
    
    // 4. 使用算法代替手动迭代
    std::for_each(vec.begin(), vec.end(), [](int n) {
        std::cout << n << " ";
    });
    std::cout << std::endl;
    
    // 5. 使用迭代器适配器简化代码
    std::vector<int> result;
    std::transform(vec.begin(), vec.end(), 
                  std::back_inserter(result),
                  [](int n) { return n * 2; });
    
    return 0;
}

10.其他

本号文章仅为个人收集总结,强烈欢迎大佬与同好指误或讨论 ^^

你可能感兴趣的:(C++STL,c++,开发语言,visualstudio,vscode)