为啥 C++ 字符串迭代器不检查错误?
Posted
技术标签:
【中文标题】为啥 C++ 字符串迭代器不检查错误?【英文标题】:Why doesn't C++ string iterator check errors?为什么 C++ 字符串迭代器不检查错误? 【发布时间】:2018-03-09 12:56:18 【问题描述】:我是一名大学生。我的任务是编写单链表。我开始使用暴露的节点(c# style)来实现它,但是在使用时会导致许多空检查。然后我将节点设为私有,列表元素通过迭代器(stl style)公开,因此没有空检查,但仍然需要检查迭代器是否不在列表末尾。我想写一个好的实现,所以做了一个简单的测试,看看c++ STL是如何处理一些错误的:
#include <iostream>
using namespace std;
int main()
string first = "afasdasds";
string second = "asdadas";
int i = 0;
for(auto it = first.begin(); i < 100; it++)
bool result = it > second.begin();
cout << result;
i++;
它没有。它在没有我检查的情况下进入无限循环。
-
迭代器不应该检查比较是否有意义吗?显然,比较来自不同列表的迭代器应该会引发错误。
不应该 ++ 运算符检查它是否没有溢出?这段代码:
using namespace std;
int main()
string first = "afasdasds";
for(auto it = first.begin();; it++)
cout << *it;
打印部分内存,然后打印段错误。 我认为这些错误检查是合理的。他们也不应该很难写。为什么它们没有在 STL 中实现,它们应该实现吗?它会被认为是良好的代码实践吗?我还没有经验,所以我很难判断代码应该有多“完美”。这是停止的好时机,还是我们应该进一步编写错误检查?
【问题讨论】:
不,它不应该 - C++ 设计理念的一部分是,你不会为你没有明确要求的东西付费。所以检查一个迭代器是否还在范围内是程序员的工作 不,没有这样的要求。程序员不能越界。 在迭代器中使用for
的正确方法是for(auto it = first.begin(); it != first.end(); ++it)
- 在我看来很简单
按理说应该是for (const auto &c : first)
。
@UnholySheep 甚至 for (auto& char : first)
然后它在引擎盖下完成:)
【参考方案1】:
这是一个权衡,性能与安全性。
检查会减慢速度。对于一个可以达到 lot 的迭代器,因为它们会在紧密循环中递增和取消引用。
STL
的许多实现都通过库的 debug 和 release 版本来缓解这种情况,这样您就可以在测试期间运行检查,但在没有检查的情况下发布。
不管STL
的基本理念是鼓励更安全的编程。如果您考虑一下,迭代器应该始终来自安全的地方。
这是在您从调用容器的begin()
或end()
函数或从算法(可能是@987654326)返回的结果中获取迭代器的过程开始时@例如。
您还可以使用更安全的构造,例如range based for。
这种编程风格有助于保持边界安全。
当你使用 indexing 或 iterator offsets 时会发生真正危险的事情,如果可能的话应该避免。存储迭代器供以后使用也不是很安全。
所以做使用迭代器并且不要使用索引(除非你必须)。尽量避免让迭代器无法立即使用。
但是,最重要的是,使用DEBUG
版本并测试、测试、测试,这样您的边界检查就可以高度自信地完成。
【讨论】:
可以说,比使用调试版本的迭代器和测试、测试、测试更有帮助的是使用当代 C++ 范例。在这种情况下:基于范围的 for 循环。 @IInspectable 好吧,我认为我的回答也涵盖了这一点,但我可以添加更多内容。 不,抱歉,您的回答中没有任何内容涉及这个问题。它只是说明不该做什么。巧合的是,基于范围的 for 循环甚至不允许您做大部分(全部?)这些事情。但是您从未明确命名该构造。test test *text*
?需要更多测试;)
@UKMonkey Thnx(我应该假装我是故意这样做的……)【参考方案2】:
迭代器不应该检查比较是否有意义吗?
...
不应该 ++ 运算符检查它是否没有溢出?
因此,如果我正确编写代码 - 我的意思是 静态,可证明是正确的,因此逻辑上不会出错 - 我是否仍然需要为您添加的所有代码支付运行时成本来检测你的错误?
您的代码应该是正确的。如果您的实现进行了这些检查并且他们曾经检测到错误,这意味着您的代码首先是错误的。拥有可以帮助您检测和修复错误的工具非常有用,但它们应该是调试辅助工具,而不是拐杖。
... 显然,比较来自不同列表的迭代器应该会引发错误
比较来自不同列表的迭代器应该引发错误:在代码审查期间。
此外,如果避免支付这些检查的运行时成本的唯一方法是避免使用标准库,那么事实上很多人将不得不避免使用标准库,而拥有一个标准库的好处有限人们实际上并没有使用。
【讨论】:
【参考方案3】:如果您的目标是调试和测试,对于 gcc,您可以使用debug containers,它作为 GNU 扩展提供。
第一个示例可以使用__gnu_debug::string
而不是std::string
重写:
#include <iostream>
#include <debug/string>
using namespace std;
int main()
__gnu_debug::string first = "afasdasds";
__gnu_debug::string second = "asdadas";
int i = 0;
for(auto it = first.begin(); i < 100; it++)
bool result = it > second.begin();
cout << result;
i++;
它正在崩溃并带有相当清晰的错误消息:
$ ./a.out
/usr/include/c++/7/debug/safe_iterator.h:658:
Error: attempt to order iterators from different sequences.
Objects involved in the operation:
iterator "lhs" @ 0x0x7ffd4285a5e0
type = __gnu_debug::_Safe_iterator<__gnu_cxx::__normal_iterator<char*, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > >, __gnu_debug::basic_string<char, std::char_traits<char>, std::allocator<char> > > (mutable iterator);
state = dereferenceable (start-of-sequence);
references sequence with type '__gnu_debug::basic_string<char, std::char_traits<char>, std::allocator<char> >' @ 0x0x7ffd4285a650
iterator "rhs" @ 0x0x7ffd4285a690
type = __gnu_debug::_Safe_iterator<__gnu_cxx::__normal_iterator<char*, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > >, __gnu_debug::basic_string<char, std::char_traits<char>, std::allocator<char> > > (mutable iterator);
state = dereferenceable (start-of-sequence);
references sequence with type '__gnu_debug::basic_string<char, std::char_traits<char>, std::allocator<char> >' @ 0x0x7ffd4285a610
Aborted (core dumped)
【讨论】:
这个调试输出特别有用,因为它还显示了 OP 甚至没有询问的内容,即完全不相关的“容器”的迭代器之间的无效比较。以上是关于为啥 C++ 字符串迭代器不检查错误?的主要内容,如果未能解决你的问题,请参考以下文章
C++:为啥我不能将一对迭代器传递给 regex_search?
C++ for-each 语句触发“向量迭代器不兼容”断言失败:this->_Getcont() == 0