Скотт Мейерс - Эффективное использование STL
- Название:Эффективное использование STL
- Автор:
- Жанр:
- Издательство:Питер
- Год:2002
- Город:СПб.
- ISBN:ISBN 5-94723-382-7
- Рейтинг:
- Избранное:Добавить в избранное
-
Отзывы:
-
Ваша оценка:
Скотт Мейерс - Эффективное использование STL краткое содержание
В этой книге известный автор Скотт Мейерс раскрывает секреты настоящих мастеров, позволяющие добиться максимальной эффективности при работе с библиотекой STL.
Во многих книгах описываются возможности STL, но только в этой рассказано о том, как работать с этой библиотекой. Каждый из 50 советов книги подкреплен анализом и убедительными примерами, поэтому читатель не только узнает, как решать ту или иную задачу, но и когда следует выбирать то или иное решение — и почему именно такое.
Эффективное использование STL - читать онлайн бесплатно полную версию (весь текст целиком)
Интервал:
Закладка:
// Использовать v и s
vector ().swap(v); // Очистить v с уменьшением емкости
string().swap(s);// Очистить s с уменьшением емкости
Остается сделать последнее замечание, относящееся к функции swap
в целом. Перестановка содержимого двух контейнеров также приводит к перестановке их итераторов, указателей и ссылок. Итераторы, указатели и ссылки, относившиеся к элементам одного контейнера, после вызова swap
остаются действительными и указывают на те же элементы — но в другом контейнере.
Совет 18. Избегайте vector
Vector как контейнер STL обладает лишь двумя недостатками. Во-первых, это вообще не контейнер STL. Во-вторых, он не содержит bool.
Объект не становится контейнером STL только потому, что кто-то назвал его таковым — он становится контейнером STL лишь при соблюдении всех требований, изложенных в разделе 23.1 Стандарта С++. В частности, в этих требованиях говорится, что если с — контейнер объектов типа Т, поддерживающий оператор [], то следующая конструкция должна нормально компилироваться:
Т *р = &с[0];// Инициализировать Т* адресом данных,
// возвращаемых оператором []
Иначе говоря, если оператор [ ] используется для получения одного из объектов Т в Container, то указатель на этот объект можно получить простым взятием его адреса (предполагается, что оператор & типа Т не был перегружен извращенным способом). Следовательно, чтобы vector был контейнером, следующий фрагмент должен компилироваться:
vector v;
bool *pb = &v[0]: // Инициализировать bool* адресом результата.
// возвращаемого оператором vector::operator[]
Однако приведенный фрагмент компилироваться не будет. Дело в том, что vector
— псевдоконтейнер, содержащий не настоящие логические величины bool, а их представления, упакованные для экономии места. В типичной реализации каждая псевдовеличина «bool», хранящаяся в псевдовекторе, занимает один бит, а восьмибитовый байт представляет восемь «bool». Во внутреннем представлении vector
булевы значения, которые должны храниться в контейнере, представляются аналогами битовых полей.
Битовые поля, как и bool, принимают только два возможных значения, но между «настоящими» логическими величинами и маскирующимися под них битовыми полями существует принципиальное различие. Создать указатель на реальное число bool можно, но указатели на отдельные биты запрещены.
Ссылки на отдельные биты тоже запрещены, что представляет определенную проблему для дизайна интерфейса vector, поскольку функция vector::operator[]
должна возвращать значение типа Т&. Если бы контейнер vector действительно содержал bool, этой проблемы не существовало бы, но вследствие особенностей внутреннего представления функция vector::operator[]
должна вернуть несуществующую ссылку на отдельный бит.
Чтобы справиться с этим затруднением, функция vector::operator[]
возвращает объект, который имитирует ссылку на отдельный бит — так называемый промежуточный объект. Для использования STL не обязательно понимать, как работают промежуточные объекты, но вообще это весьма полезная идиома С++. Дополнительная информация о промежуточных объектах приведена в совете 30 «More Effective С++», а также в разделе «Паттерн Ргоху» книги « Приемы объектно-ориентированного проектирования » [6]. На простейшем уровне vector выглядит примерно так:
template
vector {
public:
class reference {…};// Класс, генерирующий промежуточные
// объекты для ссылок на отдельные биты
reference operator[](size_type n); // operator[] возвращает
… // промежуточный объект
};
Теперь понятно, почему следующий фрагмент не компилируется: vector v;
bool *pb=&v[0]; // Ошибка! Выражение в правой части относится к типу
// vector::reference*, а не bool*
А раз фрагмент не компилируется, vector
не удовлетворяет требованиям к контейнерам STL. Да, специфика vector
особо оговорена в Стандарте; да, этот контейнер почти удовлетворяет требованиям к контейнерам STL, но «почти» не считается. Чем больше вы напишете шаблонов, предназначенных для работы с STL, тем глубже вы осознаете эту истину. Уверяю вас, наступит день, когда написанный вами шаблон будет работать лишь в том случае, если получение адреса элемента контейнера дает указатель на тип элемента; и когда этот день придет, вы наглядно ощутите разницу между контейнером и почти контейнером.
Спрашивается, почему же vector
присутствует в Стандарте, если это не контейнер? Отчасти это связано с одним благородным, но неудачным экспериментом, но позвольте мне ненадолго отложить эту тему и заняться более насущным вопросом. Итак, от vector
следует держаться подальше, потому что это не контейнер — но что же делать, когда вам действительно понадобится вектор логических величин?
В стандартную библиотеку входят два альтернативных решения, которые подходят практически для любых ситуаций. Первое решение — deque
. Контейнер deque обладает практически всеми возможностями vector (за исключением разве что reserve и capacity), но при этом deque является полноценным контейнером STL, содержащим настоящие значения bool. Конечно, внутренняя память deque не образует непрерывный блок, поэтому данные deque не удастся передать функции С, получающей массив bool (см. совет 16), но это не удалось бы сделать и с vector из-за отсутствия переносимого способа получения данных vector. (Прием, продемонстрированный для vector в совете 16, не компилируется для vector, поскольку он зависит от возможности получения на тип элемента, хранящегося в векторе, — как упоминалось выше, vector не содержит bool.)
Второй альтернативой для vector является bitset. Вообще говоря, bitset не является стандартным контейнером STL, но входит в стандартную библиотеку С++. В отличие от контейнеров STL, размер bitset (количество элементов) фиксируется на стадии компиляции, поэтому операции вставки-удаления элементов не поддерживаются. Более того, поскольку bitset не является контейнером STL, в нем отсутствует поддержка итераторов. Тем не менее bitset, как и vector, использует компактное представление каждого элемента одним битом, поддерживает функцию flip контейнера vector и ряд других специальных функций, имеющих смысл в контексте битовых множеств. Если вы переживете без итераторов и динамического изменения размеров, вероятно, bitset хорошо подойдет для ваших целей.
А теперь вернемся к благородному, но неудачному эксперименту, из-за которого появился «псевдоконтейнер» vector. Я уже упоминал о том, что промежуточные объекты часто используются при программировании на С++. Члены Комитета по стандартизации С++ знали об этом, поэтому они решили создать vector как наглядный пример контейнера, доступ к элементам которого производится через промежуточные объекты. Предполагалось, что при наличии такого примера в Стандарте у программистов появится готовый образец для построения собственных аналогов.
Читать дальшеИнтервал:
Закладка: