保持std :: list迭代器通过插入有效

时间:2011-06-03 17:10:34

标签: c++ iterator stdlist

注意:这不是我应该“使用list还是deque”的问题。这是一个关于迭代器在insert()面前的有效性的问题。


这可能是一个简单的问题而且我太过密集而无法看到正确的方法。我正在实现(无论好坏)网络流量缓冲区为std::list<char> buf,并且我将当前的读取位置保持为迭代器readpos

当我添加数据时,我会执行类似

的操作
buf.insert(buf.end(), newdata.begin(), newdata.end());

我现在的问题是,如何保持readpos迭代器有效?如果它指向旧buf的中间,那么它应该没问题(通过std :: list的迭代器保证),但通常我可能已经读取并处理了所有数据,我有readpos == buf.end() 。插入后,我希望readpos 总是指向下一个未读的字符,如果插入的话应该是第一个插入的字符。

有什么建议吗? (没有将缓冲区更改为std::deque<char>,这似乎更适合任务,如下所示。)

更新:从使用GCC4.4的快速测试中我发现deque和list在readpos = buf.end()方面的行为有所不同:在最后插入后,readpos在列表中被破坏,但指向双端队列中的下一个元素。 这是标准保证吗?

(根据cplusplus,任何deque :: insert()都会使所有迭代器失效。这没有用。可能使用计数器比迭代器更好地跟踪deque中的位置?)

4 个答案:

答案 0 :(得分:5)

if (readpos == buf.begin())
{
    buf.insert(buf.end(), newdata.begin(), newdata.end());
    readpos = buf.begin();
}
else
{
    --readpos;
    buf.insert(buf.end(), newdata.begin(), newdata.end());
    ++readpos;
}

不优雅,但应该有效。

答案 1 :(得分:4)

来自http://www.sgi.com/tech/stl/List.html

“列表具有重要的属性,即插入和拼接不会使列表元素的迭代器无效,甚至删除也只会使指向被删除元素的迭代器无效。”

因此,readpos在插入后仍应有效。

...然而

std::list< char >是一种解决此问题的非常低效的方法。存储在std::list中的每个字节都需要一个指针来跟踪字节,加上列表节点结构的大小,通常还需要两个指针。这至少是12或24字节(32或64位)的内存,用于跟踪单个字节的数据。

std::deque< char>可能是更好的容器。与std::vector类似,它在后面提供恒定的时间插入,但它也可以在前面提供恒定的时间移除。最后,像std::vector std::deque是一个随机访问容器,因此您可以使用偏移/索引而不是迭代器。这三个功能使其成为一种有效的选择。

答案 2 :(得分:0)

我的确很密集。该标准为我们提供了所需的所有工具。具体而言,序列容器要求23.2.3 / 9说:

  

迭代器从a.insert(p, i, j)点返回到插入a的第一个元素的副本,或者p如果i == j

接下来,list::insert的说明(23.3.5.4/1):

  

不影响迭代器和引用的有效性。

所以实际上如果pos是我当前正在使用的列表中的迭代器,我可以说:

auto it = buf.insert(buf.end(), newdata.begin(), newdata.end());

if (pos == buf.end()) { pos = it; }

我的列表中新元素的范围是[it, buf.end()),未处理元素的范围是[pos, buf.end())。这是有效的,因为如果pos在插入之前等于buf.end(),那么它仍然在插入之后,因为插入不会使任何迭代器无效,甚至不会结束。< / p>

答案 3 :(得分:-1)

list<char>是一种非常低效的存储字符串的方法。它可能比字符串本身大10-20倍,而且你正在为每个角色追逐一个指针......

您是否考虑过使用std::dequeue<char>

[编辑]

要回答您的实际问题,添加和删除元素不会使list中的迭代器失效......但end()仍然是end()。因此,在插入新元素以更新readpos迭代器时,您需要将其作为特殊情况进行检查。