Д. Стефенс - C++. Сборник рецептов
- Название:C++. Сборник рецептов
- Автор:
- Жанр:
- Издательство:КУДИЦ-ПРЕСС
- Год:2007
- Город:Москва
- ISBN:5-91136-030-6
- Рейтинг:
- Избранное:Добавить в избранное
-
Отзывы:
-
Ваша оценка:
Д. Стефенс - C++. Сборник рецептов краткое содержание
Данная книга написана экспертами по C++ и содержит готовые рецепты решения каждодневных задач для программистов на С++. Один из авторов является создателем библиотеки Boost Iostreams и нескольких других библиотек C++ с открытым исходным кодом. В книге затрагивается множество тем, вот лишь некоторые из них: работа с датой и временем; потоковый ввод/вывод; обработка исключений; работа с классами и объектами; сборка приложений; синтаксический анализ XML-документов; программирование математических задач. Читатель сможет использовать готовые решения, а сэкономленное время и усилия направить на решение конкретных задач.
C++. Сборник рецептов - читать онлайн бесплатно полную версию (весь текст целиком)
Интервал:
Закладка:
#include
#include
#include
#include
using namespace std;
static const int ARRAY_SIZE = 5;
template
FwdIter fixOutliersUBound(FwdIter p1,
FwdIter p2, const T& oldVal, const T& newVal) {
for ( ; p1 != p2; ++p1) {
if (greater(*p1, oldVal)) {
*p1 = newVal;
}
}
}
int main() {
list lstStr;
lstStr.push_back("Please");
lstStr.push_back("leave");
lstStr.push_back("a");
lstStr.push_back("message");
// Создать итератор для последовательного перебора элементов списка
for (list::iterator p = lstStr.begin();
p != lstStr.end(); ++p) {
cout << *p << endl;
}
// Или можно использовать reverse_iterator для перебора от конца
// к началу, rbegin возвращает reverse_iterator, указывающий
// на последний элемент, a rend возвращает reverse_iterator, указывающий
// на один-перед-первым.
for (list::reverse_iterator p = lstStr.rbegin();
p != lstStr.rend(); ++p) {
cout << *p << endl;
}
// Перебор диапазона элементов
string arrStr[ARRAY_SIZE] = {"My", "cup", "cup", "runneth", "over"};
for (string* p = &arrStr[0];
p != &arrStr[ARRAY_SIZE]; ++p) {
cout << *p << endl;
}
// Использование стандартных алгоритмов со стандартной последовательностью
list lstStrDest;
unique_copy(&arrStr[0], &arrStr[ARRAY_SIZE],
back_inserter(lstStrDest));
}
Итератор — это тип, который используется для ссылки на единственный объект в контейнере. Стандартные контейнеры используют итераторы как основной механизм для доступа к содержащимся в них элементам. Итератор ведет себя как указатель; для доступа к объекту, на который указывает итератор, вы его разыменовываете (с помощью операторов *
или ->
), а для перевода итератора вперед или назад используется синтаксис, аналогичный арифметике указателей. Однако есть несколько причин, по которым итератор — это не то же самое, что указатель. Однако перед тем, как я покажу их, давайте рассмотрим основы использования итераторов.
Итератор объявляется с помощью типа, элементы которого с его помощью будут перебираться. Например, в примере 7.1 используется list
, так что итератор объявляется вот так.
list::iterator p = lstStr.begin();
Если вы не работали со стандартными контейнерами, то часть этого объявления ::iterator
может выглядеть несколько необычно. Это вложенный в шаблон класса list typedef
, предназначенный именно для этой цели — чтобы пользователи контейнера могли создать итератор для данного конкретного экземпляра шаблона. Это стандартное соглашение, которому следуют все стандартные контейнеры. Например, можно объявить итератор для list
или для set
, как здесь.
list::iterator p1;
set::iterator p2;
Возвращаясь обратно к нашему примеру, итератор о инициализируется первым элементом последовательности, который возвращается методом begin
. Чтобы перейти к следующему элементу, используется operator++
. Можно использовать как префиксный инкремент так и постфиксный инкремент ( p++
), аналогично указателям на элементы массивов, но префиксный инкремент не создает временного значения, так что он более эффективен и является предпочтительным. Постфиксный инкремент ( p++
) должен создавать временную переменную, так как он возвращает значение p
до его инкрементирования. Однако он не может инкрементировать значение после того, как вернет его, так что он вынужден делать копию текущего значения, инкрементировать текущее значение, а затем возвращать временное значение. Создание таких временных переменных с течением времени требует все больших и больших затрат, так что если вам не требуется именно постфиксное поведение, используйте префиксный инкремент.
Как только будет достигнут элемент end
, переход на следующий элемент следует прекратить. Или, строго говоря, когда будет достигнут элемент, следующий за end
. В отношении стандартных контейнеров принято некое мистическое значение, которое представляет элемент, идущий сразу за последним элементом последовательности, и именно оно возвращается методом end
. Этот подход работает в цикле for
, как в этом примере:
for (list::iterator p = lstStr.begin();
p != lstStr.end(); ++p) {
cout << *p << endl;
}
Как только p
станет равен end
, p
больше не может увеличиваться. Если контейнер пуст, то begin == end
равно true
, и тело цикла никогда не выполнится. (Однако для проверки пустоты контейнера следует использовать метод empty
, а не сравнивать begin
и end
или использовать выражение вида size == 0
.)
Это простое объяснение функциональности итераторов, но это не все. Во-первых, как только что было сказано, итератор работает как rvalue
или lvalue
, что означает, что его разыменованное значение можно присваивать другим переменным, а можно присвоить новое значение ему. Для того чтобы заменить все элементы в списке строк, можно написать нечто подобное следующему
for (list::iterator p = lstStr.begin();
p != lstStr.end(); ++p) {
*p = "mustard";
}
Так как *p
ссылается на объект типа string
, для присвоения элементу контейнера новой строки используется выражение string::operator=(const char*)
. Но что, если lstStr
— это объект типа const
? В этом случае iterator
не работает, так как его разыменовывание дает не-const объект. Здесь требуется использовать const_iterator
, который возвращает только rvalue
. Представьте, что вы решили написать простую функцию для печати содержимого контейнера. Естественно, что передавать контейнер следует как const
-ссылку.
template
void printElements(const T& cont) {
for(T::const_iterator p = cont.begin();
p ! = cont.end(); ++p) {
cout << *p << endl;
}
}
В этой ситуации следует использовать именно const
, a const_iterator
позволит компилятору не дать вам изменить *p
.
Время от времени вам также может потребоваться перебирать элементы контейнера в обратном порядке. Это можно сделать с помощью обычного iterator
, но также имеется reverse_iterator
, который предназначен специально для этой задачи. reverse_iterator
ведет себя точно так же, как и обычный iterator
, за исключением того, что его инкремент и декремент работают противоположно обычному iterator
и вместо использования методов begin
и end
контейнера с ним используются методы rbegin
и rend
, которые возвращают reverse_iterator
. reverse_iterator
позволяет просматривать последовательность в обратном порядке. Например, вместо инициализации reverse_iterator
с помощью begin
он инициализируется с помощью rbegin
, который возвращает reverse_iterator
, указывающий на последний элемент последовательности. operator++
перемещает его назад — по направлению к началу последовательности, rend
возвращает reverse_iterator
, который указывает на элемент, находящийся перед первым элементом. Вот как это выглядит.
Интервал:
Закладка: