Программирование >>  Включение нужных заголовков 

1 ... 32 33 34 [ 35 ] 36 37 38 ... 71


beginO . end() I

Ha рисунке видно характерное смещение reverse i terator и соответствующего базового итератора, воспроизводящего смещение rbeginO и rendO по отношению к beginC) и end О, но найти на нем ответы на некоторые вопросы не удается. В частности, рисунок не объясняет, как использовать i для выполнения операций, которые должны были выполняться с ri.

насколько эффективна данная методика? Ответ прост: она эффективна настолько, насколько это позволяют итераторы. Для итераторов произвольного доступа, поддерживаемых контейнерами vector, string, deque и т. д., эта операция выполняется с постоянным временем. Для двусторонних итераторов (к этой категории относятся итераторы других стандартных контейнеров, а также некоторых реализаций хэшированных контейнеров - см. совет 25) эта операция выполняется с линейным временем.

Поскольку получение iterator, эквивалентного const iterator, может потребовать линейного времени, и поскольку это вообще невозможно сделать при недоступности контейнера, к которому относится const i terator, проанализируйте архитектурные решения, вследствие которых возникла необходимость получения iterator по const i terator. Результат такого анализа станет дополнительным доводом в пользу совета 26, рекомендующего отдавать предпочтение iterator перед const- и reverse-итераторами.

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

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

vector<int> v;

v.reserve(5): См. совет 14

for (int i=l:i<=5:++i){ Занести в вектор числа 1-5

v.push back(i);

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

find(v.rbegin().v.rend().3): vector<int>:: iterator iCri.baseO): Присвоить i результат вызова base

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

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

rend() ri() rbeginO

I , , ,1 ,1



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

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

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

1 2 3 4 5

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

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

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

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



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

vector<int> v:

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

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

find(v.rbegin().v.rend().3); v.erase(--ri .baseO): Попытка стирания в позиции.

предшествующей ri-baseO:

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

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

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

См. ранее

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

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

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

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

Совет 29. Рассмотрите возможность использования istreambufjterator при посимвольном вводе

Предположим, вы хотите скопировать текстовый файл в объект string. На первый взгляд следующее решение выглядит вполне разумно:

i fstream inputFi 1 e( iinteresti ngData.txt ):

string fileData(istreamJterator<char>(inputFile)). Прочитать inputFile istream iterator<char>()): в fileData



1 ... 32 33 34 [ 35 ] 36 37 38 ... 71

© 2006 - 2024 pmbk.ru. Генерация страницы: 0
При копировании материалов приветствуются ссылки.
Яндекс.Метрика