我有一个深度优先搜索算法,用于遍历我的图,给定起始节点的迭代器。
文档摘要:
GraphIter
是Graph::iterator
的 typedef Graph
类扩展 map<string, Node>
start->second.edges()
返回set<string>
如果start->second.edges()
的大小 0 ,此代码会导致细分错误:
(为了简洁,我已经截断了不相关的部分,包括递归调用。)
void Graph::dfs(GraphIter start)
{
cout << "EDGES SIZE: " << start->second.edges().size() << endl;
for (set<string>::iterator it = start->second.edges().begin();
it != start->second.edges().end(); ++it)
{
GraphIter iter = this->find(*it); // <--- SEGMENTATION FAULT
}
}
现在看一下当我将start->second.edges()
拉入局部变量时会发生什么:不再有段错!
这是不产生段错误的代码:
void Graph::dfs(GraphIter start)
{
set<string> edges = start->second.edges(); // <--- MAGIC TRICK
cout << "EDGES SIZE: " << edges.size() << endl;
for (set<string>::iterator it = edges.begin();
it != edges.end(); ++it)
{
GraphIter iter = this->find(*it);
}
}
不同之处在于,在优秀的代码中,当字符串集的大小(来自edges()
方法) 0 时,{在第二种情况下永远不会输入{1}}循环。但在第一种情况下,for
循环仍然至少执行一次,直到它意识到它无法取消引用for
变量。
为什么这些不同?他们不能访问相同的内存部分吗?
答案 0 :(得分:6)
由于edges()
按值返回set
,start->second.edges().begin()
和start->second.edges().end()
会将迭代器返回到不同的容器,因为每次调用edges()
都会产生新的set
1}}被退回。
通过使用命名变量创建单个副本,可以确保迭代器都来自同一个容器,并且可以从begin()
到end()
进行有效的迭代。
答案 1 :(得分:3)
可能start->second.edges()
按值返回std::set<std::string>
。这会使循环中的迭代器不兼容并导致未定义的行为。
你的“魔术”
set<string> edges = start->second.edges();
确保您在同一个容器上迭代“edge”。
您可以通过引用Node::edges()
来修复它:
const std::set<std::string>& edges() const { .... }