Совет 28. Научитесь использовать функцию base

Совет 28. Научитесь использовать функцию base

При вызове функции base для итератора reverse_iterator будет получен «соответствующий» iterator, однако из сказанного совершенно не ясно, что же при этом происходит. В качестве примера рассмотрим следующий фрагмент, который заносит в вектор числа 1-5, устанавливает reverse_iterator на элемент 3 и инициализирует iterator функцией base:

vector<int> v;

v.reserve(5);

//См. совет 14

for (int i=1;i<=5;++i){

v.push_back(i);

// Занести в вектор числа 1-5

vector<int>::reverse_iterator ri=

find(v.rbegin(),v.rend(),3);

vector<int>:: iterator i (ri.base());

// Установить ri на элемент 3

// Присвоить i результат вызова base

// для итератора ri

После выполнения этого фрагмента ситуация выглядит примерно так:

На рисунке видно характерное смещение reverse_iterator и соответствующего базового итератора, воспроизводящего смещение begin() и end() по отношению к begin() и end(), но найти на нем ответы на некоторые вопросы не удается. В частности, рисунок не объясняет, как использовать i для выполнения операций, которые должны были выполняться с ri.

Как упоминалось в совете 26, некоторые функции контейнеров принимают в качестве параметров-итераторов только iterator. Поэтому если вы, допустим, захотите вставить новый элемент в позицию, определяемую итератором п, сделать это напрямую не удастся; функция insert контейнера vector не принимает reverse_ iterator. Аналогичная проблема возникает при удалении элемента, определяемого итератором г . Функции erase не соглашаются на reverse_iterator и принимают только iterator. Чтобы выполнить удаление или вставку, необходимо преобразовать reverse_iterator в iterator при помощи base, а затем воспользоваться iterator для выполнения нужной операции.

Допустим, потребовалось вставить в v новый элемент в позиции, определяемой итератором n. Для определенности будем считать, что вставляется число 99. Учитывая, что n на предыдущем рисунке используется для перебора справа налево, а новый элемент вставляется перед позицией итератора, определяющего позицию вставки, можно ожидать, что число 99 окажется перед числом 3 в обратном порядке перебора. Таким образом, после вставки вектор v будет выглядеть так:

Конечно, мы не можем использовать n для обозначения позиции вставки, поскольку это не iterator. Вместо этого необходимо использовать i. Как упоминалось выше, когда n указывает на элемент 3, i (то есть r. base()) указывает на элемент 4. Именно на эту позицию должен указывать итератор i, чтобы вставленный элемент оказался в той позиции, в которой он бы находился, если бы для вставки можно было использовать итератор r.

Заключение:

• чтобы эмулировать вставку в позицию, заданную итератором ri типа reverse_ iterator, выполните вставку в позицию r.base(). По отношению к операции вставки ri и r.base() эквивалентны, но r.base() в действительности представляет собой iterator, соответствующий ri.

Рассмотрим операцию удаления элемента. Вернемся к взаимосвязи между ri и исходным вектором (по состоянию на момент, предшествующий вставке значения 99):

Для удаления элемента, на который указывает итератор r, нельзя просто использовать , поскольку этот итератор ссылается на другой элемент. Вместо этого нужно удалить элемент, предшествующий i.

Заключение:

• чтобы эмулировать удаление в позиции, заданной итератором ri типа reverse_ iterator, выполните удаление в позиции, предшествующей ri .base(). По отношению к операции удаления ri и ri .base() не эквивалентны, a ri .base() не является объектом iterator, соответствующим ri.

Однако к коду стоит присмотреться повнимательнее, поскольку вас ждет сюрприз:

vector<int> v;

… // См. ранее. В вектор v заносятся // числа 1-5

vector<int>::reverse_iterator ri = // Установить ri на элемент 3

find(v.rbegin(),v.rend(),3);

v.erase(--ri .base());// Попытка стирания в позиции.

// предшествующей ri-base():

// для вектора обычно

// не компилируется

Решение выглядит вполне нормально. Выражение --ri.base() правильно определяет элемент, предшествующий удаляемому. Более того, приведенный фрагмент будет нормально работать для всех стандартных контейнеров, за исключением vector и string. Наверное, он бы мог работать и для этих контейнеров, но во многих реализациях vector и string он не будет компилироваться. В таких реализациях типы iterator (и const_iterator) реализованы в виде встроенных указателей, поэтому результатом вызова i.base() является указатель. В соответствии с требованиями как С, так и С++ указатели, возвращаемые функциями, не могут модифицироваться, поэтому на таких платформах STL выражения типа --i.base() не компилируются. Чтобы удалить элемент в позиции, заданной итератором reverse_iterator, и при этом сохранить переносимость, необходимо избегать модификации возвращаемого значения base. Впрочем, это несложно. Если мы не можем уменьшить результат вызова base, значит, нужно увеличить reverse_iterator и после этого вызвать base!

//См. ранее

v.erase((++ri).base()); // Удалить элемент, на который указывает ri;

// команда всегда компилируется

Такая методика работает во всех стандартных контейнерах и потому считается предпочтительным способом удаления элементов, определяемых итератором reverse_iterator.

Вероятно, вы уже поняли: говорить о том, что функция base класса reverse_ iterator возвращает «соответствующий» iterator, не совсем правильно. В отношении вставки это действительно так, а в отношении удаления — нет. При преобразовании reverse_iterator в iterator важно знать, какие операции будут выполняться с полученным объектом iterator. Только в этом случае вы сможете определить, подойдет ли он для ваших целей.

Поделитесь на страничке

Следующая глава >

Похожие главы из других книг:

Элемент BASE

Из книги автора

Элемент BASE Если внутри документа создаются относительные ссылки на файлы, то может возникнуть ситуация, когда текущий документ перемещен и файлы становятся недоступны. Но в языке HTML есть инструменты для решения этой проблемы.Элемент BASE служит для того, чтобы задать


Приложение А Научитесь программированию за десять лет

Из книги автора

Приложение А Научитесь программированию за десять лет «Опыт, сущ.: Нечто, что вы не получаете до тех пор, пока это вам не понадобится». - Оливер - Данная глава написана Петером Норвигом (Peter Norvig, © 2001 г.). Воспроизводится по разрешению. Оригинальную статью, включая


Поддержка XML Base

Из книги автора

Поддержка XML Base Одним из пунктов, добавленных в рабочий проект XSLT 1.1, была поддержка спецификации W3C XML Base. На момент написания книги спецификация XML Base существует в форме предлагаемой рекомендации (Proposed Recommendation), датированной 20 декабря 2000 г.; текущую версию документа можно


8.15. Эхо-сервер TCP и UDP, использующий функцию select

Из книги автора

8.15. Эхо-сервер TCP и UDP, использующий функцию select Теперь мы объединим наш параллельный эхо-сервер TCP из главы 5 и наш последовательный эхо-сервер UDP из данной главы в один сервер, использующий функцию select для мультиплексирования сокетов TCP и UDP. В листинге 8.14 представлена


BASE

Из книги автора

BASE Научный интернет-поисковик Bielefeld Academic Search Engine (BASE) имеет европейские корни. Сам сервис BASE принадлежит немецкому университету, а в основе его поисковых алгоритмов лежат разработки норвежской компании Fast Search & Transfer. Ценность данного ресурса состоит в том, что BASE


Установка Netkit-base-0.17

Из книги автора

Установка Netkit-base-0.17 Приблизительное время компиляции: 0.03 SBU Необходимое дисковое пространство: 1 MBИнсталляция Netkit-baseДля инсталляции Netkit-base выполните:./configure && make && make install && cp etc.sample/{services,protocols} /etcВ директории etc.sample расположены другие занятные файлы, которые


Netkit-base

Из книги автора

Netkit-base Официальная ссылкаNetkit-base (0.17): ftp://ftp.uk.linux.org/pub/linux/Networking/netkit/Содержимое Netkit-baseПоследняя проверка: версия 0.17.Программыinetd и pingОписанияinetdinetd – родитель всех демонов. Прослушивает соединения и передает вызов соответствующему демону.pingping посылает ICMP пакеты ECHO_REQUEST


Отклик на единичную функцию

Из книги автора

Отклик на единичную функцию Единичная ступенчатая функция показана на рис. 5.20, б. По определению она остается нулевой до t=0, а начиная с этого момента становится равной 1 В. Параметры элементов для схемы, показанной на рис. 5.20, a: R=2 Ом, R1=1 Ом и С=0,125 Ф. Анализ схемы показывает,


Шаг 25 - Как сделать виртуальной свободную функцию.

Из книги автора

Шаг 25 - Как сделать виртуальной свободную функцию. Чаще всего этот прием я видел в отношении оператора operator‹‹. Точнее, не чаще, а всегда. На нем и разберем. Пусть у нас есть иерархия классов, и мы хотим определить диагностическую функцию Dump(). Она должна вываливать


Совет 16. Научитесь передавать данные vector и string функциям унаследованного интерфейса

Из книги автора

Совет 16. Научитесь передавать данные vector и string функциям унаследованного интерфейса С момента стандартизации С++ в 1998 году элита С++ настойчиво подталкивает программистов к переходу с массивов на vector. Столь же открыто пропагандируется переход от указателей char* к объектам


Совет 26. Старайтесь использовать iterator вместо const_iterator, reverse_iterator и const_reverse_iterator

Из книги автора

Совет 26. Старайтесь использовать iterator вместо const_iterator, reverse_iterator и const_reverse_iterator Как известно, каждый стандартный контейнер поддерживает четыре типа итераторов. Для контейнера container<T> тип iterator работает как Т* тогда как const_iterator работает как const Т* (также встречается запись


Базовые классы (Base)

Из книги автора

Базовые классы (Base) Следующие классы предоставляются, чтобы упростить определение типов (typedefs) параметров и результата:template ‹class Arg, class Result›struct unary_function { typedef Arg argument_type; typedef Result result_type;};template ‹class Arg1, class Arg2, class Result›struct binary_function { typedef Arg1 first_argument_type; typedef Arg2 second_argument_type; typedef


7.9.1. Тип указателя на функцию

Из книги автора

7.9.1. Тип указателя на функцию Как объявить указатель на функцию? Как выглядит формальный параметр, когда фактическим аргументом является такой указатель? Вот определение функции lexicoCompare(), которая сравнивает две строки лексикографически:#include stringint lexicoCompare( const string sl, const


Disc Video Base

Из книги автора

Disc Video Base Сайт: http://dvb.bv.ruРазмер: 90 КбайтСтатус: FreewareА эта программка (из-за ее размера назвать ее программой даже как-то язык не поворачивается) пригодится всем тем, кто «терпеть не может» органайзеры и каталогизаторы, но в то же самое время не хочет, чтобы из домашней


Научитесь правильно держать фотокамеру

Из книги автора

Научитесь правильно держать фотокамеру Для начала следует научиться правильно держать камеру и принимать нужное положение.Во-первых, следует правильно стоять. Одну ногу следует выдвинуть немного вперед, вторую — поставить сзади и чуть развернуть. Такая позиция более