1

如果我们举这个例子:

std::map<int,foo*> intmap;
fillMap(intmap);

// I will force this to end(), in real life this could be a "find" output
std::map<int,foo*>::iterator iter = intmap.end(); 
if(iter->second != 0)
    iter->second->whatever();

我在“whatever()”调用上但在“->second”空指针检查中没有出现分段错误(这是预期的,该示例没有故意检查“iter!= intmap.end()”):这是预期的行为吗?这个段会在“whatever()”调用上系统地出错,还是取决于特定的运行时内存条件?

提前感谢您的评论。贾科莫

4

4 回答 4

6

STL 容器的取消引用end()和结束迭代器是未定义的行为。没有什么可以称为预期的。一切都可能发生,它甚至可以工作。它可能取决于很多因素,例如编译器/libs/OS 版本、运行时环境状态、调试/发布构建等。因此,您永远不应该假设如果您做了不正当的事情会发生什么。

于 2012-10-24T15:42:39.537 回答
0

您不能取消引用“结束”迭代器。请记住,这与 ;iter->大致相同(*iter).。也就是说,有一个取消引用。

于 2012-10-24T15:40:46.017 回答
0

取消引用end()迭代器是一种未定义的行为。它可能会或可能不会崩溃。

您必须检查这样的返回值find

if(iter != intmap.end())
   iter->second->whatever();
于 2012-10-24T15:40:47.780 回答
0

取消引用一个过去的迭代器的结果当然是未定义的行为,所以不能保证它会做什么。

但是,考虑可能发生的情况可能具有指导意义(并且在调试场景中很有用)。关联容器的典型实现是节点的二叉树,其中每个节点包含指向迭代顺序的next和节点的指针,迭代器是指向节点的指针的薄包装器。previous类似地,list实现为双向链表,其中每个节点都包含指向nextprevious节点的指针。由于过去的迭代器需要是可递减的,最简单的实现是它指向一个始终存在的节点,其previous指针指向容器中的最后一个节点。

因为这个过去的节点总是需要存在的,即使对于一个空的容器,最简单的实现就是把它放在容器类本身里面,大多数库实现都会这样做。结果,它的存储是自动(函数本地)存储,并且它将是默认构造的,因此取消引用过去的迭代器将产生堆栈垃圾。

我们可以通过比较指针来检查:

#include <map>
#include <iostream>
int main() {
    std::map<int, int> m;
    std::cout << &m << ' ' << &*m.end() << ' ' << &m + 1 << '\n';
}

0xbf990034 0xbf990048 0xbf99004c

如您所见,过去节点的存储包含在map的堆栈占用空间中。

于 2012-10-24T15:59:50.677 回答