Д. Вандевурд, Н.М. Джосаттис - Шаблоны C++. Справочник разработчика (2003) (1160769), страница 50
Текст из файла (страница 50)
14.3). Однако если тип реализации известен во время компиляции, то вместо этого можно использовать подход с применением шаблонов (рис. 14,.4). Это приведет к большей безопасиости типов и позволит избежать использования указателей, что должно способствовать более быстрому выполнению программы.
а Злесь и далее появляется определенная путаница, связанная с тем, что в русскоязычной литеРатуре в качестве перевода терминов гетр(асе и рассеги принято одно слово — шаблон. Впрочем, кз контекста должно быль понятно, идет ли речь о шаблонах С++ (С++ сешр1асез) или о шаблонах проектирования (демйц рассешк). — Прим. ред. 266 Глава 14. Полиморфные возможности шаблонов Рис. 14З. Шаблон Впдяе рыьепв реализованный с использованием наследования Рис. 14.4.
Впдяе рзпега, реализованный с использованием шаблонов 14.5. Обобщенное программирование Статический полиморфизм порождает концепцию обобщенного программирования (яелег(с ргоягаттсля). Однако единого универсального установившегося определения этого понятия не существует (как не существует и единого установившегося определения понятия объектно-ориентированного програлаиирования). Согласно (12), имеются определения от программирования с обобщенными параметрами (ргоягатт(пя шсс)с лепелс рагатесегз) до поиска наиболее абстрактного представления эффективных алгорилииов (Дт(слб с)се том абзсгасс гергезелсасюп о1 е111с(епс а1лог(с)илз). Книга резюмирует: Обобщенное програлсмирование — это поддисциплина информатики, катарах имеет дело с поиском абстрактнвсх представлений эффективных алгоритмов структур данных и других понятий программного обеспечения, вместе с организацией их систематики....
Обобщенное программирование сосредоточивает вни мание на представлении семейств концепций доменов (стр. 169-170). В контексте С++ обобшенное программирование иногда определяется как програм мирование с шаблонами (ргоягаттсля шсссс сетр1асеь), в то время как объектноориентированное программирование рассматривается, как программирование с вирту- 14.5. Обобщенное программирование 267 альными функциями (ргоягатт/пв шай пггиа!/нпсг/опв). В этом смысле почти любое использование шаблонов в С++ можно рассматривать как пример обобщенного программирования.
Однако практикующие программисты часто рассматривают обобщенное программирование, как имеющее дополнительный существенный компонент, а именно: шаблоны должны конструироваться в целях предоставления большого числа полезных комбинаций. Наиболее значительный вклад в этой области принадлежит стандартной бнбвнопгеке шаблонов (Баии/ап/ Татр/аге УЬгагу — БТ3.4, которая позже была адаптирована и включена в стандартную библиотеку С++.
БТ1. является основой, которая предоставляет большое количество полезных операций, называемых алгоритмами, для ряда линейных структур данных для хранения коллекции обьектов (конгпейнвров (воша(пегз)). И алгоритмы и контейнеры являются шаблонами; однако ключевой момент состоит в том, что алгоритмы не являются функциями-членами контейнеров. Они написаны обобщенным способом, так что их можно использовать любым контейнером (и линейной коллекцией элементов). Для обеспечения такого использования проектировщики БТ(.
определили абстрактное понятие итераторов (йегасагв), которые могут быть предосшвлены для любого вида линейной коллекции. По существу, аспекты функционирования контейнера, специфические для данной коллекции, оказались переложенными на функциональность нтераторов. Вследствие этого операция наподобие вычисления максимального значения в последовательности может быть выполнена и без знания того, каким образом в этой последовательности хравпся значения. сешр1аге <с1авв 1хегагог> 1сегагог пах е1ешепг(1гегагог )эед, // Ссылка на начало Хсегасог епй) // и конец коллекции // Используются определенные операции итератора // для обхода всех элементов коллекции с целью // поиска элемента с максимальным значением и // возврата его позиции посредством итератора Вместо того чтобы обеспечил полезными операциями, подобными шах е1еюепс ( ), «аждый из линейных контейнеров, контейнер должен предоставить итератор для обхола всех содержащихся в ием значений, а также функции-члены„необходимые для создания мклх нтераторов.
павеврасе эсй ( сешр1асе <с1аэз Т, ... > с1аээ чессог ( ри)э11с: Сурейеб ... сонэк Бгегагог; // зависящий от реализации итератор // для постоянных векторов Глава 14. Полиморфные возможности шаблонов 268 сопвс Ткегасог Ьедйп() сопвсу // Итератор для.начала коллекции сопле йгегагог епс)() сопле; // Итератор для конца коллекции Ьетр1асе <с1авв Т, ... > с1авв 11вк ( рцЬ11с: куреней ...
сопев Тгегагог; // Зависящий от реализации итератор // для постоянных списков сопвс 1гегагог Ьед1п() сопле; // Итератор для начала коллекции сопвс 1гегасог епс)() сопвг; // Итератор для конца коллекции Теперь можно находить максимум любой коллекции, вызывая обобщенную операцию пах е1еиепс () с указанием начала и конца коллекции в качестве аргументов (здесь опущен специальный случай пустой коллекции). // ро1у/рг1пгшах.срр ()1пс1ис)е <чесгог> «1пс1цс)е «11вг> ()Тпс1цбе <а1дог1гЬт> ()йпс1ис)е <1овггеат> ()1пс1цде "ИуС1авв.Ьрр" сешр1асе <Гурепате Т> чойб ргйпк шах(Т сопвкй со11) ( // Объявление локального итератора коллекции сурепке Т::сопвс Тгегасог ров; // Вычисление позиции максимального значения ров = вМ:ивах е1етепк(со11.Ьедйп(),со11.епб()) // Вывод значения максимального элемента коллекции // (если таковой имеется) 18 (ров (= со11.епс)() ) ( 14.6.
Заключение вес)::сопс « *ров « вес)::епй1; е1ве ( вас)::соис « "ешргу" « вей::епс)1; ) 1пс пайп() ( впг):: ческог<МуС1авв> с1; вкй::11вв<МУС1авв> с2; рл1пс шах(с1)р ргйпс шах(с2); ) Параметризируя свои операции в терминах нтераторов, ЗТЬ избегает резкого увеличения количества определений операций.
Вместо того чтобы реализовать каждую операцию для каждого контейнера, нужно реализовать алгоритм всего лишь один раз, после чего его можно будет использовать для каждого контейнера. Обобщенная связка (денег(с я(ие) — это итераторы, которые обеспечиваются контейнерами и используется алгоритмами. Этот метод работоспособен, поскольку итераторы имеют определенный интерфейс, который обеспечивается контейнерами и используется алгоритмами. Этот интерфейс обычно называется концепцией (солсерг), что обозначает набор ограничений, которым должен удовлепюрять шаблон, чтобы вписаться в соответствуюшую схему. В принципе функциональность ЗТЬ может быть реализована и с использованием динамического полиморфизма.
Однако на практике этот способ имел бы весьма ограниченное использование, поскольку концепция итератора слишком "легковесна" по сравнению с механизмом вызова виртуальных функций. Добавление уровня интерфейса, основанного на виртуальных функциях, скорее всего снизит скорость выполнения наших операций на порядок (а то и более). Обобщенное программирование основано на статическом полиморфизме, при котором разрешение интерфейсов осушествляется в процессе компиляции. Однако, с другой стороны, необходимость разрешения интерфейсов в процессе компиляции требует новых принципов проектирования, которые во многом отличаются от принципов объектноориентированного проектирования. Наиболее важные из этих принципов обобщенного лроектирования описаны в оставшейся части книги.
14.6. Заключение Контейнерные типы были первым толчком для введения шаблонов в язык программирования С++. До шаблонов наиболее популярным подходом при разработке контейнеров были полиморфные иерархии. В качестве широко известного примера можно привести библиотеку классов Национального института здравоохранения (Малова! 1пзйшюз 270 Глава 14. Полиморфные возможности шаблонов о( Неа1бз С1аза Ьйзщгу — ЖНСЬ), в которой была расширена иерархия контейнеров Яшайш)к (рис.
14.5). Рис. 14.5;Иерархия кяассов ИЗИД Во многом подобно стандартной библиотеке С++, МНСЬ поддерживала широкое разнообразие контейнеров и итераторов. Однако реализация библиотеки следовала стилю динамического полиморфизма Яшай1айп для работы с коллекциями разных типов итераторы использовали абстрактный базовый класс сох з ес е хоп. К сожалению, цена такого подхода была весьма высока, это касалось как времени работы, так и используемой памяти. Обычно время работы оказывалось на порядок больше, чем у эквивалентного кода, использующего стандартную библиотеку С++, поскольку большинство операций приводили к виртуальным вызовам (в то время как в стандартно" библиотеке С++ многие операции являются встраиваемыми, а в интерфейсах итераторов и контейнеров нет никаких виртуальных функций).