我首先会问问题,然后是动机,最后是一个按预期编译和执行的说明性代码示例。
如果我可以向自己保证迭代器在我需要使用它的持续时间内不会失效,那么保持指向迭代器的指针是安全的(例如指向list<int>::iterator
的指针)。
我有多个容器,我需要从一个容器中保存的项目到另一个容器中保存的相应项目的直接交叉引用,依此类推。一个容器中的项目可能并不总是在另一个容器中具有相应的项目。
因此,我的想法是将指向迭代器的指针存储到容器#1中存储的元素#2中的元素,依此类推。为什么?因为一旦我有一个迭代器,我不仅可以访问容器#2中的元素,但如果需要,我也可以删除容器#2中的元素等。
如果容器#2中有相应的元素,我将在容器#1的元素中存储一个指向迭代器的指针。否则,此指针将设置为NULL。现在我可以快速检查一下,如果指向迭代器的指针是NULL,那么容器#2中没有相应的元素,如果非NULL,我可以继续访问它。
那么,以这种方式存储指向迭代器的指针是否安全?
#include <iostream>
#include <list>
using namespace std;
typedef list<int> MyContainer;
typedef MyContainer::iterator MyIterator;
typdef MyIterator * PMyIterator;
void useIter(PMyIterator pIter)
{
if (pIter == NULL)
{
cout << "NULL" << endl;
}
else
{
cout << "Value: " << *(*pIter) << endl;
}
}
int main()
{
MyContainer myList;
myList.push_back(1);
myList.push_back(2);
PMyIterator pIter = NULL;
// Verify for NULL
useIter(pIter);
// Get an iterator
MyIterator it = myList.begin();
// Get a pointer to the iterator
pIter = & it;
// Use the pointer
useIter (pIter);
}
答案 0 :(得分:12)
迭代器通常按值处理。例如,begin()
和end()
将返回类型iterator
的实例(对于给定的迭代器类型),而不是iterator&
,因此它们返回副本每次都有价值。
您当然可以为此副本提供一个地址,但您不能指望对begin()
或end()
的新呼叫将返回具有相同地址的对象,并且该地址仅有效当你自己坚持迭代器对象时。
std::vector<int> x { 1, 2, 3 };
// This is fine:
auto it = x.begin();
auto* pi = ⁢
// This is not (dangling pointer):
auto* pi2 = &x.begin();
维护指向迭代器的指针很少有意义:迭代器是已经轻量级的数据句柄。进一步的间接通常是设计不佳的标志。在你的例子中,指针毫无意义。只需传递一个普通的迭代器。
答案 1 :(得分:1)
迭代器的问题在于容器上有很多操作使它们失效(哪一个取决于所讨论的容器)。当你将迭代器保存到属于另一个类的容器时,你永远不知道何时发生这样的操作,并且没有简单的方法可以发现迭代器现在是无效的。
此外,直接删除属于另一个类的容器中的元素是违反封装原则的。如果要删除另一个类的数据,最好调用该类的公共方法,然后删除该数据。
答案 2 :(得分:1)
是的,它是安全的,只要你能确保迭代器不会失效并且不会超出范围。
答案 3 :(得分:1)
听起来很吓人。迭代器是一个对象,如果它离开作用域,则指针无效。如果擦除容器#2中的对象,则所有迭代器都可能变为无效(取决于容器),因此指针变得无用。
为什么不存储迭代器本身?对于容器#1中没有引用任何内容的元素,请存储container2.end()。 只要迭代器没有失效,这就没问题。如果是,则需要重新生成映射。
答案 4 :(得分:1)
是的,可以处理指向迭代器的指针,就像它对其他类型一样,但在你的例子中没有必要,因为你可以简单地传递原始迭代器作为参考传递。
通常,存储迭代器不是一个好主意,因为在修改容器时迭代器可能会变得无效。更好地存储容器并根据需要创建迭代器。