C++ STL string 迭代器的使用详解

迭代器是 C++ STL 的核心概念,它为容器提供了统一的访问接口。对于 std::string,迭代器提供了一种安全、高效的方式来遍历和操作字符串中的字符。本文将全面介绍 string 迭代器的使用方法、类型、操作和最佳实践。

图片[1]_C++ STL string 迭代器的使用详解_知途无界

1. 迭代器的基本概念

1.1 什么是迭代器?

迭代器是一种对象,它能够遍历容器中的元素,类似于指针。对于 std::string,迭代器提供了对字符序列的顺序访问。

1.2 string 迭代器的优势

  • 通用性​:与指针操作相比,迭代器语法更统一、更安全
  • 泛型编程​:可以在不了解容器内部实现的情况下编写通用算法
  • 安全性​:边界检查(使用 at() 或调试模式)可以避免越界访问

2. string 迭代器的类型

2.1 四种主要的迭代器类型

#include <iostream>
#include <string>
#include <iterator>
using namespace std;

int main() {
    string str = "Hello, World!";
    
    // 1. 普通迭代器(可读可写)
    string::iterator it_normal = str.begin();
    
    // 2. 常量迭代器(只读,不能修改元素)
    string::const_iterator it_const = str.cbegin();
    
    // 3. 反向迭代器(从后向前遍历)
    string::reverse_iterator it_reverse = str.rbegin();
    
    // 4. 常量反向迭代器(只读的反向遍历)
    string::const_reverse_iterator it_const_reverse = str.crbegin();
    
    cout << "Original string: " << str << endl;
    return 0;
}

2.2 迭代器类型的选择指南

迭代器类型声明方式读写权限遍历方向使用场景
普通迭代器string::iterator可读可写从前向后需要修改字符串内容
常量迭代器string::const_iterator只读从前向后只读访问,保证数据安全
反向迭代器string::reverse_iterator可读可写从后向前需要逆序修改
常量反向迭代器string::const_reverse_iterator只读从后向前只读逆序访问

3. 基本迭代器操作

3.1 获取迭代器

#include <iostream>
#include <string>
using namespace std;

int main() {
    string str = "Hello";
    
    // 获取首元素迭代器
    string::iterator begin_it = str.begin();
    string::const_iterator cbegin_it = str.cbegin();
    
    // 获取尾后迭代器(指向最后一个元素之后的位置)
    string::iterator end_it = str.end();
    string::const_iterator cend_it = str.cend();
    
    // 获取反向迭代器
    string::reverse_iterator rbegin_it = str.rbegin();
    string::reverse_iterator rend_it = str.rend();
    
    // C++11 统一初始化语法
    auto auto_begin = str.begin();
    auto auto_end = str.end();
    
    cout << "String: " << str << endl;
    cout << "First char: " << *begin_it << endl;        // H
    cout << "Last char: " << *(str.end() - 1) << endl; // o
    cout << "Reverse first: " << *rbegin_it << endl;   // o
    
    return 0;
}

3.2 迭代器运算

#include <iostream>
#include <string>
#include <algorithm>
using namespace std;

int main() {
    string str = "Programming";
    
    // 算术运算
    auto it = str.begin();
    it++;                    // 前进一个位置
    it += 3;                 // 前进三个位置
    it--;                    // 后退一个位置
    it -= 2;                 // 后退两个位置
    
    cout << "After operations: " << *it << endl; // g
    
    // 比较运算
    if (str.begin() == str.begin()) {
        cout << "Same iterator" << endl;
    }
    
    if (str.begin() != str.end()) {
        cout << "Different iterators" << endl;
    }
    
    // 解引用和成员访问
    string::iterator it2 = str.begin();
    cout << "Dereference: " << *it2 << endl;        // P
    cout << "Array access: " << it2[0] << endl;      // P
    cout << "Next char: " << *(it2 + 1) << endl;     // r
    
    return 0;
}

4. 遍历 string 的各种方法

4.1 使用迭代器遍历

#include <iostream>
#include <string>
using namespace std;

// 方法1:传统for循环
void traverse_with_for_loop(const string& str) {
    cout << "Traditional for loop: ";
    for (string::const_iterator it = str.begin(); it != str.end(); ++it) {
        cout << *it;
    }
    cout << endl;
}

// 方法2:while循环
void traverse_with_while_loop(const string& str) {
    cout << "While loop: ";
    string::const_iterator it = str.begin();
    while (it != str.end()) {
        cout << *it;
        ++it;
    }
    cout << endl;
}

// 方法3:C++11 auto关键字
void traverse_with_auto(const string& str) {
    cout << "Using auto: ";
    for (auto it = str.begin(); it != str.end(); ++it) {
        cout << *it;
    }
    cout << endl;
}

// 方法4:范围for循环(基于迭代器实现)
void traverse_with_range_for(const string& str) {
    cout << "Range-based for: ";
    for (char c : str) {
        cout << c;
    }
    cout << endl;
}

// 方法5:反向迭代器
void traverse_reverse(const string& str) {
    cout << "Reverse traversal: ";
    for (auto it = str.rbegin(); it != str.rend(); ++it) {
        cout << *it;
    }
    cout << endl;
}

int main() {
    string text = "Iterator";
    
    traverse_with_for_loop(text);
    traverse_with_while_loop(text);
    traverse_with_auto(text);
    traverse_with_range_for(text);
    traverse_reverse(text);
    
    return 0;
}

4.2 使用 STL 算法与迭代器

#include <iostream>
#include <string>
#include <algorithm>
#include <cctype>
using namespace std;

// 使用find算法查找字符
void find_char(const string& str, char target) {
    auto it = find(str.begin(), str.end(), target);
    if (it != str.end()) {
        cout << "Found '" << target << "' at position: " << distance(str.begin(), it) << endl;
    } else {
        cout << "Character '" << target << "' not found" << endl;
    }
}

// 使用count算法统计字符出现次数
void count_chars(const string& str, char target) {
    int cnt = count(str.begin(), str.end(), target);
    cout << "Character '" << target << "' appears " << cnt << " times" << endl;
}

// 使用transform算法转换大小写
void convert_case(string& str) {
    transform(str.begin(), str.end(), str.begin(), ::toupper);
    cout << "Uppercase: " << str << endl;
    
    transform(str.begin(), str.end(), str.begin(), ::tolower);
    cout << "Lowercase: " << str << endl;
}

// 使用remove_if算法删除特定字符
void remove_spaces(string& str) {
    auto new_end = remove_if(str.begin(), str.end(), ::isspace);
    str.erase(new_end, str.end());
    cout << "Without spaces: " << str << endl;
}

int main() {
    string text = "Hello World! Programming is fun.";
    
    cout << "Original: " << text << endl;
    
    find_char(text, 'W');
    count_chars(text, 'm');
    convert_case(text);
    
    string text2 = "C++ Iterators Are Powerful";
    remove_spaces(text2);
    
    return 0;
}

5. 修改字符串内容

5.1 通过迭代器修改字符

#include <iostream>
#include <string>
using namespace std;

void modify_with_iterators() {
    string str = "abcdefghij";
    
    cout << "Original: " << str << endl;
    
    // 修改单个字符
    string::iterator it = str.begin();
    *it = 'A';  // 将第一个字符改为'A'
    cout << "After first modification: " << str << endl;
    
    // 修改连续字符
    fill(str.begin(), str.begin() + 3, 'X');
    cout << "After filling first 3 chars: " << str << endl;
    
    // 使用迭代器插入字符
    str.insert(str.begin() + 5, 'Y');
    cout << "After insertion: " << str << endl;
    
    // 使用迭代器删除字符
    str.erase(str.begin() + 2, str.begin() + 5);
    cout << "After erasure: " << str << endl;
}

// 使用transform修改字符
void transform_string(string& str) {
    transform(str.begin(), str.end(), str.begin(),
        [](char c) { return islower(c) ? toupper(c) : tolower(c); });
    // 这个lambda将小写转大写,大写转小写
}

int main() {
    modify_with_iterators();
    
    string mixCase = "Hello World";
    cout << "Mixed case: " << mixCase << endl;
    transform_string(mixCase);
    cout << "Transformed: " << mixCase << endl;
    
    return 0;
}

5.2 迭代器与算法结合的常见模式

#include <iostream>
#include <string>
#include <algorithm>
#include <numeric>
using namespace std;

// 累加字符串中所有字符的ASCII值
void accumulate_ascii(const string& str) {
    int sum = accumulate(str.begin(), str.end(), 0);
    cout << "Sum of ASCII values: " << sum << endl;
}

// 查找满足条件的子串
void find_pattern(const string& str) {
    // 查找连续的大写字母序列
    auto it = adjacent_find(str.begin(), str.end(),
        [](char a, char b) { return isupper(a) && isupper(b); });
    
    if (it != str.end()) {
        cout << "Found consecutive uppercase letters starting at: " 
             << distance(str.begin(), it) << endl;
    }
}

// 部分反转字符串
void partial_reverse(string& str, int start, int end) {
    if (start < 0 || end > str.size() || start >= end) return;
    
    reverse(str.begin() + start, str.begin() + end);
}

int main() {
    string text = "AbCdEfGhIj12345";
    
    accumulate_ascii(text);
    find_pattern(text);
    
    partial_reverse(text, 2, 8);
    cout << "Partially reversed: " << text << endl;
    
    return 0;
}

6. 高级迭代器技巧

6.1 迭代器适配器

#include <iostream>
#include <string>
#include <iterator>
#include <algorithm>
using namespace std;

void iterator_adapters_demo() {
    string str = "Hello Iterator";
    
    // 使用istream_iterator从输入读取
    cout << "Enter a string: ";
    istream_iterator<char> input_iter(cin);
    istream_iterator<char> eos; // 流结束迭代器
    
    string from_input;
    copy(input_iter, eos, back_inserter(from_input));
    cout << "You entered: " << from_input << endl;
    
    // 使用ostream_iterator输出
    cout << "Characters printed individually: ";
    ostream_iterator<char> output_iter(cout);
    copy(str.begin(), str.begin() + 5, output_iter); // 输出前5个字符
    cout << endl;
    
    // 使用back_inserter在容器末尾插入
    string dest;
    copy(str.begin(), str.end(), back_inserter(dest));
    cout << "Copied string: " << dest << endl;
}

// 使用插入迭代器修改原字符串
void using_insert_iterators() {
    string str = "Start";
    string to_insert = "_Middle_End";
    
    // 在指定位置插入
    auto insert_pos = str.begin() + 5; // "Start"的结尾
    copy(to_insert.begin(), to_insert.end(), inserter(str, insert_pos));
    
    cout << "After insertion: " << str << endl; // Start_Middle_End
}

6.2 自定义函数对象与迭代器

#include <iostream>
#include <string>
#include <algorithm>
using namespace std;

// 自定义函数对象,用于统计元音字母
struct VowelCounter {
    int count = 0;
    void operator()(char c) {
        c = tolower(c);
        if (c == 'a' || c == 'e' || c == 'i' || c == 'o' || c == 'u') {
            count++;
        }
    }
};

// 函数对象,用于移除特定字符
struct CharRemover {
    char to_remove;
    CharRemover(char c) : to_remove(tolower(c)) {}
    
    bool operator()(char c) const {
        return tolower(c) == to_remove;
    }
};

void custom_functors_demo() {
    string text = "Hello World Programming";
    
    // 使用函数对象统计元音
    VowelCounter counter = for_each(text.begin(), text.end(), VowelCounter());
    cout << "Number of vowels: " << counter.count << endl;
    
    // 使用函数对象移除特定字符
    string no_l = "Hello World Programming";
    no_l.erase(remove_if(no_l.begin(), no_l.end(), CharRemover('l')), no_l.end());
    cout << "Without 'l': " << no_l << endl;
}

// 使用lambda表达式(现代C++推荐)
void lambda_with_iterators() {
    string text = "Hello World 123";
    
    // 移除所有数字
    text.erase(remove_if(text.begin(), text.end(),
        [](char c) { return isdigit(c); }), text.end());
    cout << "Without digits: " << text << endl;
    
    // 查找第一个元音字母的位置
    auto vowel_pos = find_if(text.begin(), text.end(),
        [](char c) { 
            c = tolower(c);
            return c == 'a' || c == 'e' || c == 'i' || c == 'o' || c == 'u';
        });
    
    if (vowel_pos != text.end()) {
        cout << "First vowel at position: " << distance(text.begin(), vowel_pos) << endl;
    }
}

7. 迭代器失效问题

7.1 常见的迭代器失效情况

#include <iostream>
#include <string>
using namespace std;

void iterator_invalidation_demo() {
    string str = "abcdefghij";
    
    // 危险:在循环中修改字符串可能导致迭代器失效
    cout << "Unsafe iteration: ";
    for (auto it = str.begin(); it != str.end(); ++it) {
        cout << *it;
        if (*it == 'c') {
            str.erase(it); // 危险!it现在可能失效
            // 继续执行++it可能导致未定义行为
        }
    }
    cout << endl;
    
    // 安全的方法1:使用返回值更新迭代器
    cout << "Safe method 1: ";
    str = "abcdefghij"; // 重置
    auto it = str.begin();
    while (it != str.end()) {
        cout << *it;
        if (*it == 'c') {
            it = str.erase(it); // erase返回下一个有效的迭代器
        } else {
            ++it;
        }
    }
    cout << endl;
    
    // 安全的方法2:使用remove-erase惯用法
    cout << "Safe method 2: ";
    str = "abcdefghij"; // 重置
    str.erase(remove(str.begin(), str.end(), 'c'), str.end());
    cout << str << endl;
}

// 插入操作也可能导致失效(但通常只影响超出容量的迭代器)
void insertion_invalidation() {
    string str = "abc";
    auto it = str.begin() + 1; // 指向'b'
    
    cout << "Before insertion: " << str << ", iterator points to: " << *it << endl;
    
    str.insert(str.begin() + 2, 'X'); // 在'c'前插入'X'
    // 对于string,插入通常不会使现有迭代器失效(除非重新分配内存)
    // 但最好重新获取迭代器以确保安全
    cout << "After insertion: " << str << endl;
}

8. 性能考虑与最佳实践

8.1 迭代器 vs 下标访问

#include <iostream>
#include <string>
#include <chrono>
using namespace std;

void performance_comparison() {
    string large_str(1000000, 'a'); // 100万个'a'
    
    // 使用下标访问计时
    auto start1 = chrono::high_resolution_clock::now();
    for (size_t i = 0; i < large_str.size(); ++i) {
        char c = large_str[i];
    }
    auto end1 = chrono::high_resolution_clock::now();
    
    // 使用迭代器访问计时
    auto start2 = chrono::high_resolution_clock::now();
    for (auto it = large_str.begin(); it != large_str.end(); ++it) {
        char c = *it;
    }
    auto end2 = chrono::high_resolution_clock::now();
    
    auto duration1 = chrono::duration_cast<chrono::microseconds>(end1 - start1);
    auto duration2 = chrono::duration_cast<chrono::microseconds>(end2 - start2);
    
    cout << "Subscript access: " << duration1.count() << " μs" << endl;
    cout << "Iterator access: " << duration2.count() << " μs" << endl;
    cout << "Difference: " << abs(duration1.count() - duration2.count()) << " μs" << endl;
}

8.2 最佳实践总结

  1. ​**优先使用 const_iterator**​:当不需要修改元素时,使用 const_iteratorcbegin()/cend() 以保证安全。
  2. 使用 auto 简化代码​:现代 C++ 中推荐使用 auto 来推导迭代器类型。
  3. 注意迭代器失效​:在修改容器(插入、删除)后,要小心处理迭代器。
  4. 使用算法而非手写循环​:STL 算法通常更高效、更安全。
  5. 边界检查​:在调试模式下使用 _GLIBCXX_DEBUG 或类似的调试工具来检查迭代器越界。
  6. 选择合适的迭代器类型​:
    • 需要修改元素:用普通迭代器
    • 只读访问:用 const_iterator
    • 逆序遍历:用反向迭代器

9. 综合示例

#include <iostream>
#include <string>
#include <algorithm>
#include <vector>
#include <cctype>
using namespace std;

// 综合示例:实现一个简单的字符串分析工具
class StringAnalyzer {
public:
    // 统计单词数量(简单实现,以空格分隔)
    static int count_words(const string& str) {
        int count = 0;
        bool in_word = false;
        
        for (auto it = str.begin(); it != str.end(); ++it) {
            if (isspace(*it)) {
                if (in_word) {
                    count++;
                    in_word = false;
                }
            } else {
                in_word = true;
            }
        }
        if (in_word) count++; // 最后一个单词
        return count;
    }
    
    // 提取所有唯一字符并保持顺序
    static string unique_chars(const string& str) {
        string result;
        for (auto it = str.begin(); it != str.end(); ++it) {
            if (find(result.begin(), result.end(), *it) == result.end()) {
                result.push_back(*it);
            }
        }
        return result;
    }
    
    // 反转单词顺序(保持单词内部字符顺序)
    static string reverse_words(const string& str) {
        string result;
        auto word_start = str.end();
        
        for (auto it = str.begin(); it != str.end(); ++it) {
            if (isspace(*it)) {
                if (word_start != str.end()) {
                    // 找到一个单词的结束
                    if (!result.empty()) result.push_back(' ');
                    copy(word_start, it, back_inserter(result));
                    word_start = str.end();
                }
            } else {
                if (word_start == str.end()) {
                    word_start = it; // 标记单词开始
                }
            }
        }
        
        // 处理最后一个单词
        if (word_start != str.end()) {
            if (!result.empty()) result.push_back(' ');
            copy(word_start, str.end(), back_inserter(result));
        }
        
        // 现在反转整个字符串中的单词
        reverse(result.begin(), result.end());
        return result;
    }
};

int main() {
    string text = "C++ Iterators Are Very Powerful And Flexible";
    
    cout << "Original text: " << text << endl;
    cout << "Word count: " << StringAnalyzer::count_words(text) << endl;
    cout << "Unique characters: " << StringAnalyzer::unique_chars(text) << endl;
    cout << "Reversed words: " << StringAnalyzer::reverse_words(text) << endl;
    
    return 0;
}

总结

std::string 迭代器提供了强大而灵活的字符串访问方式。通过掌握:

  • 四种迭代器类型及其适用场景
  • 基本操作​(算术运算、比较、解引用)
  • 与 STL 算法的配合使用
  • 修改字符串内容的技巧
  • 避免迭代器失效的注意事项

你可以编写出更安全、更高效、更易维护的字符串处理代码。在现代 C++ 开发中,熟练使用迭代器和基于迭代器的算法是必备技能。

© 版权声明
THE END
喜欢就点个赞,支持一下吧!
点赞80 分享
评论 抢沙发
头像
欢迎您留下评论!
提交
头像

昵称

取消
昵称表情代码图片

    暂无评论内容