Д. Стефенс - C++. Сборник рецептов
- Название:C++. Сборник рецептов
- Автор:
- Жанр:
- Издательство:КУДИЦ-ПРЕСС
- Год:2007
- Город:Москва
- ISBN:5-91136-030-6
- Рейтинг:
- Избранное:Добавить в избранное
-
Отзывы:
-
Ваша оценка:
Д. Стефенс - C++. Сборник рецептов краткое содержание
Данная книга написана экспертами по C++ и содержит готовые рецепты решения каждодневных задач для программистов на С++. Один из авторов является создателем библиотеки Boost Iostreams и нескольких других библиотек C++ с открытым исходным кодом. В книге затрагивается множество тем, вот лишь некоторые из них: работа с датой и временем; потоковый ввод/вывод; обработка исключений; работа с классами и объектами; сборка приложений; синтаксический анализ XML-документов; программирование математических задач. Читатель сможет использовать готовые решения, а сэкономленное время и усилия направить на решение конкретных задач.
C++. Сборник рецептов - читать онлайн бесплатно полную версию (весь текст целиком)
Интервал:
Закладка:
vector vec;
Здесь важно запомнить, что vector
хранит значения , не обращая внимания на то, что они означают. Следовательно, он не знает, что для указателей перед их удалением следует использовать delete
. Если выделить память, затем поместить указатели в память vector
, то по окончании работы следует самостоятельно удалить память. Не дайте ввести себя в заблуждение термину «контейнер», думая, что если в vector
сохранить указатель, то это подразумевает владение им.
После удаления указателей следует явно очистить vector
— по той же причине, по которой следует присваивать переменным-указателям по окончании работы с ними значение NULL. Это предотвратит ошибочное повторное удаление.
6.5. Хранение объектов в списке
Требуется хранить элементы в виде последовательности, но vector не соответствует всем требованиям. В частности, требуется иметь возможность эффективно добавлять и удалять элементы в середине последовательности, а не только в ее конце.
Для хранения данных используйте list
, объявленный в . list
предлагает более высокую производительность и большую гибкость при изменении последовательности в произвольных местах. Пример 6.5 показывает, как использовать list
, а также демонстрирует некоторые из его уникальных операций.
Пример 6.5. Использование list
#include
#include
#include
#include
using namespace std;
// Простая функция для печати
template
struct printer {
void operator()(const T& s) {
cout << s << '\n';
}
};
bool inline even(int n) {
return(n % 2 == 0);
}
printer strPrinter;
printer intPrinter;
int main() {
list lstOne;
list lstTwo;
lstOne.push_back("Red");
lstOne.push_back("Green");
lstOne.push_back("Blue");
lstTwo.push_front("Orange");
lstTwo.push_front("Yellow");
lstTwo.push_front("Fuschia");
for_each(lstOne.begin(), // Напечатать каждый элемент списка,
lstOne.end(), // используя пользовательскую функцию печати
strPrinter);
lstOne.sort(); // list содержит методы для сортировки
lstTwo.sort();
lstOne.merge(lstTwo); // Объединить два списка и напечатать
for_each(lstOne.begin(), // результаты (перед объединением списки должны
lstOne.end(), // быть отсортированы)
strPrinter);
list intLst;
intLst.push_back(0);
intLst.push_back(1);
intLst.push_back(2);
intLst.push_back(3);
intLst.push_back(4);
// Удалить все значения больше 2
intLst.remove_if(bind2nd(greater(), 2));
for_each(intLst.begin(), intLst.end(), intPrinter);
// Или удалить все четные значения
intLst.remove_if(even);
}
list
— это последовательность, обеспечивающая постоянную сложность операций вставки и удаления элементов в произвольную позицию, но обладающая линейной сложностью их поиска. Обычно list
реализуется как двухсвязный список, что означает, что каждый элемент хранится в узле, содержащем указатели на предыдущий и следующий элементы последовательности. Он обеспечивает все требования к контейнеру стандартной последовательности, полюс предоставляет несколько уникальных методов.
Объявление list
не вызывает сложностей — просто укажите тип элементов, которые в нем будут храниться, и, опционально, класс распределителя памяти.
list
typename Allocator = allocator > // Используемый распределитель
// памяти
Параметр шаблона Value
— это тип элементов, которые будут храниться в list
. Это должен быть тип, который поддерживает конструктор копирования и присвоение. Allocator
— это используемый класс распределителя памяти. По умолчанию используется стандартный распределитель (и в большинстве случаев его вполне достаточно).
Далее приведено типичное объявление list
(см. пример 6.5).
list lstOne;
После объявления списка поместите в него что-нибудь с помощью push_front
или push_back
, как здесь.
lstOne.push_back("Red"); // Добавление элементов в конец списка
lstOne.push_back("Green");
lstOne.push_back("Blue");
lstTwo.push_front("Orange"); // Добавление элементов в начало
lstTwo.push_front("Yellow");
lstTwo.push_front("Fuschia");
Помещение элементов в list
занимает постоянное время, а не амортизированное постоянное время , как в случае с vector
. Реализации list
не требуется периодически изменять размер буфера, так что в них не будет возникать периодических падений производительности, как при использовании vector
. list
просто должен обновить набор указателей, и больше ничего.
Для удаления элементов из начала или конца list
используйте pop_front
или pop_back
(без аргументов). Несмотря на их имя, методы «pop» не возвращают извлекаемый элемент, как это можно ожидать, исходя из обычной семантики стеков.
Обычно list
выглядит так, как показано на рис. 6.2. Каждый узел содержит (по меньшей мере) три части: объект, в нем содержащийся, указатель на предыдущий узел и указатель на следующий узел. В оставшейся части рецепта я буду ссылаться на указатели на следующий и предыдущий узлы как next_
и prev_
.

Рис. 6.2. Список с двунаправленными связями
Когда вы увидите, как реализован list
, станет очевидно, почему некоторые операции имеют сложность, отличную от их сложности для vector
. Добавление элемента в любое место list
требует только изменения указателей next_
и prev_
предыдущего и следующего элементов. Приятным моментом в list
является то, что при вставке и удалении элементов в помощью insert
и erase
устаревают значения только тех итераторов, которые указывают на затрагиваемый(е) объект(ы). Итераторы для других элементов не теряют актуальности.
Методы вставки и удаления — это insert
и erase
, insert
в качестве первого аргумента принимает итератор, а в качестве второго — либо объект типа T
, либо количество и затем объект типа T
, либо начальный и конечный итераторы. Первый аргумент-итератор указывает на элемент, непосредственно перед которым должна произойти вставка. Перегрузки insert
используются вот так.
list strlst;
list::iterator p;
// ...
string s = "Scion";
p = find(strLst.begin(), strLst.end(), // std::find из
"Toyota");
strLst.insert(p, s); // Вставить s сразу перед p
strLst.insert(p, 16, s); // Вставить 16 копий s непосредственно перед p
strLst insert(p, myOtherStrLst.begin(), // Вставить все, что содержится
myOtherStrLst.end()); // в myOtherStrLst, перед p
Удаление элементов аналогично.
Читать дальшеИнтервал:
Закладка: