Бьерн Страуструп. Язык программирования С++. Специальное издание (2011) (1004033), страница 110
Текст из файла (страница 110)
Таким образом, данный подход далек от того, что требуется для построения стандартной библиотеки. Однако гибкость и универсальность этого подхода не нужно недооценивать. Его можно с успехом использовать во многих прикладных программах, где не столь важна эффективность, а нужна простота и универсальность, обеспечиваемая единым интерфейсом класса Солга1лег и реализациями универсальных служб, вроде ввода/вывода объектов.
16.2.3. Контейнеры ВТС Контейнеры и итераторы стандартной библиотеки (часто называемые ЯТ1.-ядром этой библиотеки, Э3.10) можно считать попыткой объединения лучших черт двух традиционных моделей, рассмотренных выше. Однако на самом деле библиотека БТЕ разрабатывалась иначе — она была построена в результате целенаправленного поиска бескомпромиссно эффективных обобщенных алгоритмов. Такое стремление к эффективности привело к переходу от плохо поддающихся встраиванию виртуальных функций к небольшим, часто используемым функциям доступа (асеева гппсгюпз).
Поэтому мы не можем представить стандартный интерфейс к контейнерам или стандартный интерфейс итераторов как абстрактный класс. Вместо этого, каждый тип контейнера поддерживает стандартный набор ба- 532 Глава 1б. Организация библиотеки и контейнеры зовых операций. Для того чтобы избежать проблемы жирных интерфейсов (з16.2.2, 524.4.3), операции, которые невозможно эффективно реализовать для всех контейнеров, исключаются из стандартного набора общих операций. К примеру, для яесгог обращение по индексу введено, а для йа — нет. Дополнительно, каждый вид контейнеров определяет свои собственные итераторы, предоставляющие стандартный набор операций.
Стандартные контейнеры не имеют общего базового класса. Вместо этого каждый контейнер полностью реализует стандартный контейнерный интерфейс. Аналогично, не существует общего базового класса для итераторов. Стандартные контейнеры и итераторы не обеспечивают никакой проверки типов (явной или неявной) во время выполнения. Важный и сложный вопрос с обеспечением полезных общих сервисов для всех контейнеров решен посредством аллокаторов (а!1осагогз), передаваемых в виде шаблонных параметров (а не посредством общего базового класса) (з19.4.3). У БТ1 -подхода к построению контейнеров есть свои достоинства и недостатки: + Каждый конкретный контейнер прост и эффективен (не столь прост, как контейнер индивидуального дизайна, но столь же эффективен).
з- Каждый контейнер обеспечивает набор стандартных операций со стандартными именами и семантикой. Дополнительные операции по мере необходимости реализуются индивидуально отдельными типами контейнеров. Кроме того, классы-обертки (ткгаррег с1аззез) (Э25.7.1) применяются для интеграции индивидуальных контейнеров в общую среду разработки (916.5[141). + Дополнительная степень универсальности использования контейнеров достигается с помощью итераторов.
Каждый контейнер предоставляет итераторы, поддерживающие набор стандартных операций со стандартными именами и семантикой (смыслом). Тип итераторов определяется индивидуально для каждого вида контейнеров так, чтобы итераторы были как можно проще и эффективнее. + Кроме стандартных итераторов для удовлетворения разных нужд разных контейнеров могут вводиться различные дополнительные итераторы и обобщенные интерфейсы.
ь По умолчанию контейнеры безопасны в отношении типов и однородны (все элементы контейнеров имеют одинаковый тип). Гетерогенные контейнеры могут создаваться как однородные контейнеры указателей на общий базовый класс. + Контейнеры неинтрузивны (от их элементов не требуется иметь специальный родительский класс или связующее поле). Неинтрузивные контейнеры хорошо работают как со встроенными типами, так и со стлруктлурами, раскладка которых жестко задается извне. + Интрузивные контейнеры можно интегрировать в общую среду разработки, но они все равно будут накладывать ограничения на элементы. -~ Каждый контейнер принимает шаблонный аргумент аИосагег, используемый как средство реализации общих сервисов для всех контейнеров.
Это упрощает создание таких сервисов, как долговременное хранение объектов и объектный ввод/вывод (519.4.3). 16.3. Контейнер типа чес(о( — Не существует ста(шартного представления контейнеров и итераторов для передачи их в функции в качестве аргументов с контролем ошибок времени выполнения (но для конкретных приложений это сделать нетрудно; 519.3). Как и ранее, + означает достоинство, а знак — означает недостаток. Итак, контейнеры и итераторы не имеют фиксированного стандартного представления. Вместо этого, каждый контейнер предоставляет стандартный интерфейс в виде стандартного набора операций, так что такие контейнеры взаимозаменяемы.
То же справедливо и для итераторов. Все это обеспечивает минимум нагрузки на память и максимум производительности при сохранении общности и на уровне контейнеров (как в случае подхода с общим базовым классом), и на уровне итераторов (как в случае специализированных контейнеров).
БТ1.-подход в огромной степени опирается на шаблоны. Чтобы избежать излишнего реплицирования кода, для реализации контейнеров указателей обычно применяется частичная специализация (з13.5). 16.3. Контейнер типа чес1ог Мы используем контейнерный тип гес(ог как пример законченного стандартного контейнера. Все, что явно не оговаривается, относится и ко всем остальным стандартным контейнерам.
В главе 17 рассматриваются специальные черты контейнеров !!я(, яе(, шар и т.д. Средства, предлагаемые контейнером гес(о( (и другими аналогичными контейнерами), рассматриваются довольно подробно. Цель — понять возможности применения векторов и их роль в общем дизайне стандартной библиотеки. Обзор стандартных контейнеров и нх средств можно найти в З17.1. Ниже мы представляем контейнер гес(ог поэтапно: типы, итераторы, доступ к элементам, конструкторы, стековые и списковые операции, размер и емкость, вспомогательные функции (1(е!рег й(пс(1опя) и специализация гес(ог<Ьоо(>. 16З.1.
Типы (етрЫе<с(аяя Т, с(аяя А = а((оса(ог< Т» с!аяя я(((( (иес(ог ( риЬДс( //типы( (уре((еу Т гад(е (уре; (уре((еГА а!(оса(ог (уре; (уре((еГ~репате А: (я!яе гуре яае (уре( (уре((е((урепате А (: (((фегепсе (уре (((()егепсе (уре( // тип элементов // тип менеджера памяти (урезе!' (тр(степ(аиоп ((ереп((еп(! Нега(ог( // Г" (уре((еГ!тр(степ(а((оп ((ерепдеп(2 сопя( Мега(ог; //сопя( Т» (уре((еГяи(( (гегегяе Вега(ог<йега(ог> гегегяе Вега!ог( (уре((е) я(((( (гечегяе Ыега(ог<сопя( йега1ог> сопя( гегегяе пега(ог( Стандартный контейнер гес(ог является шаблоном, определенным в пространстве имен м(! и расположенным в заголовочном файле <гес(ог>. Сначала он определяет ряд стандартных имен типов: яурезгеТ)урепате А:: рогпяег рояпяег з УУ указатель на элемент гуре«(е~яурепате А:: сопля ро(пяег сопи ро(п<егз (урез(е~гурепате А: ге~егепсе ге~егепсег УУ ссылка на элемент яурейеТгурепате А::сопля ге~егепсе сопля ге~егепсез уу ...
)' С> яурепате С:: ка)ие гуре яит (сопя( Ся с) гетр)иге< с)аяя ( яурепате С: зурепате С: катив гуре я = В; оотг йегагог р = с.Ьея(п () з УУ стартуем с самого начала У подсчет ведем до самого конца епгг() ) згвйе (р! =с ( » —: р ««рз гегигп я; гУ получаем значение элемента У теперь р указывает на следующий элем-т Каждый стандартный контейнер определяет эти имена типов в качестве своих членов. Каждый определяет их способом, наиболее приемлемым для его реализации.
Тип элементов контейнера передается в виде первого параметра шаблона и известен как га1ие гуре. Тип аПосагог гуре (тип аллокаторов — менеджеров-распределителей памяти), который факультативно передается вторым параметром шаблона, определяет, как га!ие ~ре взаимодействует с различными механизмами управления памятью. В частности, аллокатор предоставляет функции, которые контейнер использует для вьшеления/освобождения памяти под свои элементы. Аллокаторы изучаются в 5194. Как правило, тип яяее гуре используется для индексации элементов контейнера, а й9егепсе гуре — для типа результата вычитания двух итераторов одного и того же контейнера.
В общем случае эти типы соответствуют я)ее г и рггйф' г (56.2.1). В приложении Е рассматривается, как ведет себя вектор, если распределители памяти (аллокаторы) или операции с элементами генерируют исключения. С итераторами мы познакомились в 52.7.2, а более подробно они рассматриваются в главе 19. О них можно думать, как об указателях на элементы контейнеров.
Каждый контейнер определяет тип )гегагог для адресации своих элементов. Тип сопят Мгагог применяется тогда, когда элементы используются только «для чтения», Как и в случае со встроенными указателями, безопаснее применять константные версии итераторов, если нет причин поступать иначе. Фактические типы итераторов зависят от реализации. Их очевидными определениями для традиционных векторов являются типы Т" и соотг Т", соответственно. Типы обратных итераторов для векторов конструируются из стандартного шаблона гегегяе )гегагог (819.2.5). Они представляют последовательность элементов (интервал) в обратном порядке.
Как показано в 53.8.1, знание имен типов, определенных в контейнере, позволяет пользователю писать программы для работы с контейнером, ничего при этом не зная о фактических типах. Особо отметим, что это позволяет написать код, который будет работать с любым стандартным контейнером.
Например: 16.3, Контейнер типа нес(ог 16.3.2. Итераторы Как было показано в предыдущих разделах, итераторы позволяют пользователю осуществлять навигацию по элементам контейнера без знания применяемых фактических типов. Немногочисленные ключевые функции-члены позволяют пользователю фиксировать концы последовательности элементов: гетр!асс<с!азз Т, с!азз А=айосасог<Т» сгазз чессог ( рибгсс с ~7 ... й йес а(оган Вегасог Ьеясп () с йуказывает на первый элемент соил( ссегасог Ьелсп () сопзсс йегаи~ епсг(); й'указьсвает на элемент, расположенный за последним сопзс Вегасог епсг() сопзес гечегзе Вегасог гЬеесп (); йуказывает на первый элем-т обратной последоват-ти сопзс гечегзе Вегасог гЬедса () сопзсс гечегзе Вегасог гепсг(); дуказьсвает на элемент, расположенный за последним о' элементом обратной последовательности сапы гечегзе иегасог кепс(() сопла( й...
Пара функций Ьеяси() Сепсисы предоставляет доступ к элементам контейнера в обычном порядке. То есть за нулевым элементом следует первый элемент, затем второй элемент и т.д. Пара функций гЬеи(и () Сгеис(() предоставляет элементы в обратном порядке. То есть за (и-2)-ым элементом следует (и-2)-ой элемент, затем (и-3)-ий и т.д.