Сохранение итераторов std:: list допустимыми через вставку
Примечание: это не вопрос, Должен ли я "использовать List или deque". Это вопрос о валидности итераторов перед лицом insert()
.
это может быть простой вопрос, и я просто слишком туп, чтобы увидеть правильный способ сделать это. Я реализую (к лучшему или худшему) буфер сетевого трафика как std::list<char> buf
, и я сохраняю свою текущую позицию чтения в качестве итератора readpos
.
когда я добавляю данные, я что-то делаю как
buf.insert(buf.end(), newdata.begin(), newdata.end());
мой вопрос теперь, как мне сохранить readpos
итератор действует? Если он указывает на середину старого buf
, тогда все должно быть хорошо (по гарантиям итератора для std:: list), но обычно я могу читать и обрабатывать все данные, и у меня есть readpos == buf.end()
. После вставки, я хочу!--3--> всегда указать на следующий непрочитанный символ, который в случае вставки должен быть первым вставлен один.
какие предложения? (Коротко изменение буфера на std::deque<char>
, который, как представляется, гораздо лучше подходит для этой задачи, как предлагается ниже.)
обновление: из быстрого теста с GCC4.4 я замечаю, что дек и лист ведут себя по-разному в отношении readpos = buf.end()
: после вставки в конце readpos разбивается в списке, но указывает на следующий элемент в deque. это стандартная гарантия?
(по cplusplus, любой deque:: вставить() недействительным все итераторы. Это нехорошо. Может быть, использование счетчика лучше, чем итератор для отслеживания позиции в deque?)
4 ответов
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;
}
Не элегантный, но он должен работать.
от 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
контейнер произвольного доступа поэтому вы можете используйте смещения / индексы вместо итераторов. Эти три особенности делают его эффективным выбором.
Я действительно был тупым. Стандарт дает нам все необходимые инструменты. В частности, требования к контейнеру последовательности 23.2.3 / 9 гласят:
iterator, возвращенный из
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()
до вставки, после чего его еще после вставки, после вставки не лишает любой итераторы, даже не конец.
list<char>
- Это очень неэффективный способ хранения строки. Вероятно, он в 10-20 раз больше самой строки, плюс вы преследуете указатель для каждого символа...
вы рассматривали возможность использования std::dequeue<char>
вместо?
[edit]
чтобы ответить на ваш фактический вопрос, добавление и удаление элементов не делает недействительными итераторы в list
... Но!--3--> по-прежнему будет end()
. Поэтому вам нужно будет проверить это как особый случай в укажите, куда вы вставляете новый элемент, чтобы обновить свой readpos
итератор.