注意:这不是我应该“使用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中的位置?)
答案 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
迭代器时,您需要将其作为特殊情况进行检查。