Программирование на Visual C++. Архив рассылки - Алекс Jenter
Шрифт:
Интервал:
Закладка:
Q. Хотелось бы побольше узнать о предварительном просмотре. В русской программе он смотрится инородным телом на своем иностранном языке. Можно ли его как-то настраивать под себя?
В этой же связи: не могу решить проблему.
В программе 3 меню и, соответственно, 3 панели инструментов, которые создал в Create. Переключая меню, вызываю ShowControlBar – прячу ненужные панели и показываю необходимую. Но после вызова PRINT PREVIEW, в окне появляются сразу все 3 панели инструментов.
Попутно: что означает AFX_IDS_PREVIEW_CLOSE в String Table?
Serg PetukhovУспехов!
Алекс Jenter [email protected] Красноярск, 2001.Программирование на Visual C++
Выпуск №37 от 18 марта 2001 г.
Приветствую, уважаемые подписчики!
Сегодня нас ждет новая статья нашего постоянного автора Александра Шаргина, на этот раз посвященная стандартной библиотеке шаблонов C++.
СТАТЬЯ
Введение в STL
Часть 1
Автор: Александр Шаргин
[email protected]
Стандартная библиотека шаблонов (Standard Template Library, STL) входит в стандартную библиотеку языка "C++". В неё включены реализации наиболее часто используемых контейнеров и алгоритмов, что избавляет программистов от рутинного переписывания их снова и снова. При разработке контейнеров и применяемых к ним алгоритмов (таких как удаление одинаковых элементов, сортировка, поиск и т. д.) часто приходится приносить в жертву либо универсальность, либо быстродействие. Однако разработчики STL поставили перед собой сверхзадачу – сделать библиотеку одновременно эффективной и универсальной. Надо признать, что им удалось достичь цели, хотя для этого и пришлось использовать наиболее продвинутые возможности языка C++, такие как шаблоны и перегрузка операторов.
В этой статье мы рассмотрим основные концепции, которые легли в основу STL. Я не буду приводить подробных примеров использования векторов, списков и ассоциативных массивов, так как их хватает в каждом учебнике. Вместо этого я постараюсь показать, как устроена STL, для чего нужны её основные компоненты и как они взаимодействуют друг с другом, а также как расширить стандартную библиотеку, добавив в неё новые контейнеры и алгоритмы.
Стандарт языка C++ не регламетнирует реализацию контейнеров и алгоритмов STL. Поэтому с каждым компилятором поставляется своя реализация этой библиотеки. В последующем изложении я буду опираться на реализацию, поставляемую фирмой Microsoft вместе с компилятором Visual C++ 6.0. Тем не менее, большая часть сказанного будет справедлива и для других реализаций STL.
Основные концепции STLКраеугольными камнями STL являются понятия контейнера (container), алгоритма (algorithm) и итератора (iterator).
• Контейнер – это хранилище объектов (как встроенных, так и определённых пользователем типов). Простейшие виды контейнеров (статические и динамические массивы) встроены непосредственно в язык C++. Кроме того, стандартная библиотека включает в себя реализации таких контейнеров, как вектор (vector), список (list), очередь (deque), ассоциативный массив (map), множество (set), и некоторых других.
• Алгоритм – это функция для манипулирования объектами, содержащимися в контейнере. Типичные примеры алгоритмов – сортировка и поиск. В STL реализовано порядка 60 алгоритмов, которые можно применять к различным контейнерам, в том числе к массивам, встроенным в язык C++.
• Итератор – это абстракция указателя, то есть объект, который может ссылаться на другие объекты, содержащиеся в контейнере. Основные функции итератора – обеспечение доступа к объекту, на который он ссылается (разыменование), и переход от одного элемента контейнера к другому (итерация, отсюда и название итератора). Для встроенных контейнеров в качестве итераторов используются обычные указатели. В случае с более сложными контейнерами итераторы реализуются в виде классов с набором перегруженных операторов.
Рассмотрим эти концепции более подробно.
ИтераторыИтераторы используются для доступа к элементам контейнера так же, как указатели – для доступа к элементам обычного массива. Как мы знаем, в языке C++ над указателями можно выполнять следующий набор операций: разыменование, инкремент/декремент, сложение/вычитание и сравнение. Соответственно, любой итератор реализует все эти операции или некоторое их подмножество. Кроме того, некоторые итераторы позволяют работать с объектами в режиме "только чтение" или "только запись", тогда как другие предоставляют доступ и на чтение, и на запись. В зависимости от набора поддерживаемых операций различают 5 типов итераторов, которые приведены в следующей таблице.
Тип итератора Доступ Разыменование Итерация Сравнение Итератор вывода (output iterator) Только запись * ++ Итератор ввода (input iterator) Только чтение *, –> ++ ==, != Прямой итератор (Forward iterator) Чтение и запись *, –> ++ ==, != Двунаправленный итератор (bidirectional iterator) Чтение и запись *, –> ++, -- ==, != Итератор с произвольным доступом (random-access iterator) Чтение и запись *, –>, [] ++, --, +, –, +=, –= ==, !=, <, <=, >, >=Итератор с произвольным доступом реализует полный набор операций, применимых к обычным указателям.
КонтейнерыКак мы уже знаем, контейнер предназначен для хранения объектов. Хотя внутреннее устройство контейнеров очень сильно различается, каждый контейнер обязан предоставить строго определённый интерфейс, через который с ним будут взаимодействовать алгоритмы. Этот интерфейс обеспечивают итераторы. Каждый контейнер обязан иметь соответствующий ему итератор (и только итератор). Важно подчеркнуть, что никакие дополнительные функции-члены для взаимодействия алгоритмов и контейнеров не используются. Это сделано потому, что стандартные алгоритмы должны работать в том числе со встроенными контейнерами языка C++, у которых есть итераторы (указатели), но нет ничего, кроме них. Таким образом при написании собственного контейнера реализация итератора – необходимый минимум.
Каждый контейнер реализует определённый тип итераторов. При этом выбирается наиболее функциональный тип итератора, который может быть эффективно реализован для данного контейнера. "Эффективно" означает, что скорость выполнения операций над итератором не должна зависеть от количества элементов в контейнере. Например, для вектора реализуется итератор с произвольным доступом, а для списка – двунаправленный. Поскольку скорость выполнения операции [] для списка линейно зависит от его длины, итератор с произвольным доступом для списка не реализуется.
Вне зависимости от фактической организации контейнера (вектор, список, дерево) хранящиеся в нём элементы можно рассматривать как последовательность. Итератор первого элемента в этой последовательности вгозвращает функция begin(), а итератор элемента, следующего за последним – функция end(). Это очень важно, так как все алгоритмы в STL работают именно с последовательностями, заданными итераторами начала и конца.
Кроме обычных итераторов в STL существуют обратные итераторы (reverse iterator). Обратный итератор отличается тем, что просматривает последовательность элементов в контейнере в обратном порядке. Другими словами, операции + и – у него меняются местами. Это позволяет применять алгоритмы как к прямой, так и к обратной последовательности элементов. Например, с помощью функции find можно искать элементы как "с начала", так и "с конца" контейнера.
Каждый класс контейнера, реализованный в STL, описывает набор типов, связанных с контейнером. При написании собственных контейнеров следует придерживаться этой же практики. Вот список наиболее важных типов:
• value_type — тип элемента
• size_type — тип для хранения числа элементов (обычно size_t)
• iterator — итератор для элементов контейнера
• key_type — тип ключа (в ассоциативном контейнере)
Помимо типов можно выделить набор функций, которые реализует почти каждый контейнер в STL. Они не требуются для взаимодействия с алгоритмами, но их реализация улучшает взаимозаменяемость контейнеров в прграмме. Если, к примеру, какой-то контейнер реализует набор характерных для списка функций, то его можно будет вставить в программу вместо списка, изменив в ней всего одну строчку. Список основных функций приведён в таблице.
Функция Описание begin, end Возвращают итераторы начала и конца прямой последовательности. rbegin, rend Возвращают итераторы начала и конца обратной последовательности. front, back Возвращают ссылки на первый и последний элемент, хранящийся в контейнере. push_back, pop_back Позволяют добавить или удалить последний элемент в последовательности. push_front, pop_front Позволяют добавить или удалить первый элемент в последовательности. size Возвращает количество элементов в контейнере. empty Проверяет, есть ли в контейнере элементы. clear Удаляет из контейнера все элементы. insert, erase Позволяют вставить или удалить элемент(ы) в середине последовательности. АлгоритмыМы уже установили две важные вещи. Во-первых, алгоритмы предназначены для манипулирования элементами контейнера. Во-вторых, любой алгоритм рассматривает содержимое контейнера как последовательность, задаваемую итераторами первого и следующего за последним элементов. Итераторы обеспечивают интерфейс между контейнерами и алгоритмами, благодаря чему и достигается гибкость и универсальность библиотеки STL.