Как STL заказал контейнеры, знающие их конец?
Я знаю, что стандарт не определяет способ, которым должны быть реализованы контейнеры STL, а скорее задает набор требований для каждого из них.
Однако широко известно, что заказанные контейнеры STL обычно реализуются как красно-черные деревья.
Вы можете выполнять итерацию через элементы std::set
или std::map
с помощью своих соответствующих итераторов или с С++ 11 с использованием диапазонных циклов.
Что меня озадачивает, так это то, как упорядоченный контейнер в STL "знает",
все кончено". Или иначе, поскольку они реализованы как деревья,
как реализуется конец контейнера или может быть
реализованы?
Я знаю, что стандарт диктует §23.2.1/c Общие требования к контейнеру (Emphasis Mine):
begin() возвращает итератор, ссылающийся на первый элемент в контейнер. end() возвращает итератор, который является последним значением для контейнера. Если контейнер пуст, тогда begin() == end();
ОК, для смежных контейнеров это легко, но как это "прошлое-end" может быть реализовано для деревьев?
Ответы
Ответ 1
Я только что ознакомился с реализацией контейнера map
в Visual Studio 2013 STL, и вот как реализуется end
. Когда map
строится, выделяется элемент заголовка дерева RB, и этот элемент объявляется концом контейнера.
Когда вы проходите контейнер через действительный итератор, operator++
и operator--
просто пропустите элемент head. И когда вы достигаете последнего элемента дерева и увеличиваете итератор, он поднимается вверх (ищет правильное поддерево) и в конечном итоге достигает головки дерева, которая end
.
Ответ 2
Для всех контейнеров типа "list-like" для этого нужно иметь какой-то дозорный node для конца, потому что пользователь может получить end()
, вставить что-то в контейнер, уменьшить итератор и уменьшить end()
должен указывать на вставленный элемент. Я понимаю, что некоторые реализации будут динамически распределять для этого, а некоторые будут помещать динамический сторожевой элемент node внутри самого контейнера.