【问题标题】:Why use string::iterator rather than index? [duplicate]为什么使用 string::iterator 而不是索引? [复制]
【发布时间】:2010-12-31 23:51:04
【问题描述】:

可能重复:
Why use iterators instead of array indices?

string::iterator it;
for (it = str.begin(); it < str.end(); it++) 
    cout << *it;
cout << endl;

为什么不:

for (int i = 0; i < str.size(); i++)
    cout << str[i];
cout << endl;

似乎 string::iterator 也不提供范围检查。为什么要使用string::iterator 而不是索引?

谢谢。

【问题讨论】:

  • @jcyang:除了其他人的答案,请确保您养成在循环中使用预增量和迭代器的习惯。即使用 ++it 而不是 it++。预增量不会创建不必要的临时变量。
  • 除了 Jagannath 的评论之外,在将迭代器与 end() 进行比较时,更喜欢 operator!=() 而不是 operator&lt;()
  • @jcyang:后自增的典型实现是MyIterator operator++(int) { MyIterator temp(*this); ++*this; return temp; }。无论是否分配,这都会产生不必要的临时性。我隐含地假设我们也覆盖了(覆盖?覆盖?)预增量。
  • @jcyang:最好使用++i。因为如果您稍后更改使用的类型,那么您无需担心更改代码。无论循环变量的类型是什么,您现在都将始终拥有最有效的版本。
  • @jcyang:迭代器(通常)没有为它们定义运算符

标签: c++ string iterator


【解决方案1】:

在 C++ 中,您可以通过多种不同的方式做很多事情。这是又一个例子。 在这种情况下,使用哪种方法没有区别。但总的来说,迭代器更快、更安全,并且在不同类型的容器中提供更大的灵活性。

【讨论】:

    【解决方案2】:

    两者都有效。

    主要原因是一致性:您通过请求迭代器并使其前进,以相同的方式迭代集合或字符串的字符。

    我不会说++it 的实现细节导致指针增量与涉及指针算术的str[i] 相比值得一提。范围检查也是实现细节。

    【讨论】:

      【解决方案3】:

      in this question 所述,size() 方法不保证为 O(1)

      【讨论】:

      • 该线程似乎不清楚。一条评论提到s.end() - s.begin() 肯定具有恒定的复杂性,因此必须发疯地实现更复杂的 size()。
      • 为什么?大小可以基于类似 strlen() 的 O(stringLength)。为了在 O(1) 中实现 size(),您需要内存...
      • s.size() 不能用类似 strlen() 的方式实现,因为 std::string 可以包含任何字符(包括 '\0')。此外,字符串必须跟踪它的结束位置(或其大小)。如果字符串不知道它的长度并且必须在 O(N) 中找到结尾,你为什么认为 s.end() 和迭代器会更好?
      • @UncleBens:如果字符串不存储其大小(不需要这样做,但可能会这样做)。然后找到 size() 将是 O(n)。与使用迭代器 begin() 和 end() 查找大小一样(执行 end() - begin() 可能不是直接算术,因为不需要字符串中的连续内存)。但是第二个循环不计算大小,它只是不断增加迭代器直到它到达然后结束。
      • @LokiAstari:basic_string 的存储空间应该是连续的。见LWG issue #530
      【解决方案4】:

      迭代器是标准接口。通过使用迭代器,您可以对不同的容器使用相同的算法。是否使用它们的最终决定取决于可用性和可读性。

      例如,使用标准变换算法将std::string转换为大写:

      std::string str = "A String";
      std::transform(str.begin(), str.end(), str.begin(), ::toupper);
      

      将导致str 等于"A STRING"

      【讨论】:

      • 具体的string::iterator怎么样?比如有什么好处吗?
      • @jcyang:只有当你使用迭代器时,它才能在任何需要迭代器的地方工作,并且字符串可以更改为 char 数组或向量或其他容器,你的循环仍然可以工作.
      • 在上面的例子中,std::string::begin() 方法返回一个std::string::iterator,与std::string::end() 一样。好处是您可以使用std::transform 算法。您不能将其与索引一起使用。
      • 如何迭代 UTF-8 字符(不是 8 位字符)? std::strings 是否有 UTF-8 迭代器(我猜迭代器的类型应该是 uint16_t 或 uint32_t)。如何遍历希腊字母字符串“\u03b4\u03b8\u03c6”?
      【解决方案5】:

      迭代器更安全并提供更多灵活性,正如其他人发布的那样。此外,索引只能用于(有效)支持 random 的容器 访问(即直接访问给定位置的元素)。迭代器是一个更一般的概念。迭代器提供对链表、文件和许多其他数据结构的有效遍历。它通常会导致生成更高效的代码。

      【讨论】:

      • 迭代器并不快。使用它们进行的每个测试都表明它们稍微慢了一点。
      • 谢谢,我已经更正了我的帖子。
      【解决方案6】:

      如果你不知道你正在迭代哪个类(因为它是一个模板参数),你应该使用迭代器,因为不是每个提供迭代器的类也提供[](而且不是每个提供迭代器的类确实提供了[],提供了一个在O(1)时间内工作的)。因此,通过使用迭代器,您将确保该函数可以与尽可能多的类一起使用(尽管不能与 C 数组一起使用)。

      在这种特定情况下,除了个人偏好或过早优化之外,我认为没有理由更喜欢其中一个。

      【讨论】:

        【解决方案7】:

        索引只能用于支持随机访问的容器——直接访问给定位置。

        迭代器提供了一种访问任何集合/数据结构的统一方式。重构代码时的灵活性是巨大的。

        【讨论】:

          【解决方案8】:

          复制:

          1. Iterators.. why use them?
          2. Why use iterators instead of array indices?

          也就是说,这是通用性的问题。与使用数组访问相比,使用 STL 的迭代器可以做更多很多。此外,如果您需要重构代码,并将字符串更改为向量、列表或rope,则根本不需要重写代码。

          最后还有迭代中的安全问题。如果您想在循环中访问 NEXT 字符 in,则可以使用迭代器安全地执行此操作,但增加数组下标可能会在最后一个元素上出现段错误,因此需要再次检查。

          【讨论】:

          • 尝试访问(取消引用)循环中的下一个字符并不比使用数组下标更安全!两者都需要检查以确保您尚未处于字符串末尾。
          【解决方案9】:

          特别是对于 std::string,我建议您使用索引,因为它支持随机访问,而且这种方式更简单。它“推荐”使用迭代器的唯一原因是因为迭代器提供了一个标准接口来访问序列,因此如果您的序列更改为 std::list 例如,您的迭代代码将保持不受影响

          【讨论】:

          • 另请注意,迭代器可以无效,而索引则不能。
          猜你喜欢
          • 2011-02-22
          • 2013-07-25
          • 2023-04-08
          • 1970-01-01
          • 2020-09-22
          • 1970-01-01
          • 2018-08-05
          • 2018-10-29
          • 1970-01-01
          相关资源
          最近更新 更多