STL容器存在容器更改时迭代器无效的问题。容器是否可以通过添加调用has_changed()来宣布它已更改?
在某些操作之前查询empty()是很常见的。如果容器在操作上设置bool会影响迭代器,如insert()或erase(),则可以在重用迭代器之前查询has_changed()。
疯狂?
编辑感谢您提供一系列良好的回复和深思。我希望我能奖励一位以上的获胜者。
答案 0 :(得分:5)
如果用心良苦,有点疯狂。
我认为问题的主要问题是容器如何知道它何时“未更改”?换句话说,删除某些内容并设置“已更改”标志。什么是重置旗帜的事件,因为它恢复到正常或稳定的状态?它实际上是迭代器而不是处于无效状态的容器。
我认为,要实现这一点,所有迭代器都需要比现在更加智能,并且更像操作容器的Observers。容器可以将事件发送到已更改的已注册迭代器,并且可以在尝试操作之前检查自己的状态。但即使这样有很多漏洞,也可能导致比你想要解决的问题更糟糕。
答案 1 :(得分:3)
快速迭代器在Java中工作的(近似)方式是容器每次更改时都会递增计数器。迭代器在创建时复制此计数器,并在每次通过它们更改容器时递增它。如果迭代器检测到不匹配,则会抛出异常。
C ++具有令人兴奋的属性,某些操作会使容器上的某些迭代器无效,而其他操作则无效。例如,假设已保留足够的空间vector::insert
使插入点之后的迭代器无效,而不是之前。
另一个疑难案例是list::remove
。这使得所有迭代器都有效,除了删除的那个,及其所有副本。
正如您可以想象的那样,准确追踪这一点非常困难。在实践中发生的事情是,您的实现可能提供调试选项,迭代器将尽力检测它们是否有效。这可能取决于它们当前是否“正常”的实现细节,而不是标准保证它们是否仍然有效。
可以用C ++中的某些东西做类似于Java中的“版本号”,但它会给迭代器带来误报,这些迭代器看起来无效但实际上是有效的。
答案 2 :(得分:3)
迭代器失效的规则很清楚,如果你按照规则付款,你不应该需要在容器无效时询问容器。
此外,迭代器并不总是同时失效。从向量中间删除元素只会使该元素及其后的元素无效。但是指向向量开头的迭代器保持有效。
在列表中,迭代器通常只在它们指向的特定节点被销毁时才会失效。
所以你不能询问容器你的迭代器是否有效。
通用标准库实现可以选择启用类似于您请求的额外运行时检查,尽管实现更复杂(必须是,为了正确),并且会损害性能。
答案 3 :(得分:2)
这将是低效的,因为容器需要A)提供另一个数据字段并且B)相应地更新它。然而,STL被认为是在尝试做不可能的事情并将效率与抽象结合起来。 (它成功了,我应该补充一下。)
如果您需要将引用保留在更改的容器中,请使用不会使迭代器无效的容器(std::list
,std::set
)或将索引保留为std::vector
。
答案 4 :(得分:2)
这在技术上是可行的,并且MSVC实现了提供类似功能的“已检查迭代器”(http://msdn.microsoft.com/en-us/library/aa985965.aspx)。虽然迭代器变为无效时没有收到通知,并且您无法直接查询状态(我知道),但会抛出异常和/或调用invalid_parameter
,具体取决于构建的配置方式。
然而,它显然是非标准的并且显着地达到了性能。它对调试很有用。
答案 5 :(得分:0)
容器是否可以通过添加呼叫has_changed()来宣布它已更改?
我认为,是的,它可以实施。这是一个非常简单的尝试(不是那么优雅,但仍然):
bool has_changed(std::vector<int> &v)
{
static std::map<void*, size_t> has_changed_info;
void *ptr = &v;
std::map<void*, size_t>::iterator it = has_changed_info.find(ptr);
if (it == has_changed_info.end())
{
has_changed_info[ptr] = v.capacity();
return false;
}
if ( it->second != v.capacity() )
{
it->second = v.capacity();
return true;
}
return false;
}
将代码测试为:
int main() {
std::vector<int> v;
has_changed(v);
for ( int i = 0 ; i < 100 ; ++i )
{
v.push_back(i);
if (has_changed(v))
cout << "changed at " << i << endl;
}
return 0;
}
输出(GCC-4.3.4):
changed when inserting i = 0
changed when inserting i = 1
changed when inserting i = 2
changed when inserting i = 4
changed when inserting i = 8
changed when inserting i = 16
changed when inserting i = 32
changed when inserting i = 64