А. Александреску - Современное проектирование на C++ (1119444), страница 22
Текст из файла (страница 22)
Теперь можно написать следующий код. ы1дйестпто оЬ); зпс х = г1е1д«0>(оЬ)).ча1ое; О первый целый тип 1пс х г1е1д<1>(оЬ)).ча1ое; О второй целый тип Шаблонный класс аепвсаееегН(егагсну очень удобен для генерации сложных классов на основе списков простых типов. Класс сеп5саССегидегагсну можно применять для генерации виртуальных функций для каждого типа в списке.
В главе 9, посвященной абстрактным фабрикам, этот класс используется для генерации абстрактных производящих функций, работающих со списками типов. В этой главе также показано, как реализуются иерархии, порожленные классом Сепвсассеги)егагсну. 3. Т3.2. Генерация кортежей Иногда возникает необходимость создать небольшую структуру, состоящую нз безымянных полей, известную в некоторых языках (например, в языке М(.) под названием картеле (Сцр!е). Созлание кортежей на языке С++ впервые было описано в работе Якко Ярви ()аЫго )агч1, 1999а), а затем уточнено в работе ()апд апд Резче)1, 1999Ь). Что такое кортеж? Рассмотрим следующий пример.
севр1асе <с1азз т> зсгисс но1дег Т ча1ое суредет Сеп5саССеги1егагсну< тчгеьс5т 3(1пс, 1пс, 1пс), 91 Глава 3. Списки типов но1дег> Ро)пт30; Работать с классом Розпт30 ловольно трудно, поскольку после каждой функции доступа к полям нсобхолимо указывать суффикс .ча1це .
Нам нужно создать структуру, похожую на класс Еепвсаттегнзегагспу, в которой функции доступа Рзе1д возвращают ссылки непосредственно на члены ча1ие . Это значит, что функция Рзе1д<п> должна возвращать нс но1дег<зпс>, а з птй. В библиотеке 101ц определен класс тцр1е, реализованный аналогично классу ОепБсаттегнзегагспу, но предоставляющий прямой доступ к полям. Этот класс работает следующим образом. туреде1 тир1е<ттРЕЬтвт 3 О пт, з пт, 3 пт) > Роз пт30~ Ро)пс30 рс; Рзе1д<0>(рт) = О; Рбе1д<1>(рт) = 100; Рзе1д<2>(рт) = 300; Кортежи полезны для созлания небольших безымянных сТруктур, нс имеющих функций-членов.
Например, с их помощью можно возвращать из функции сразу несколько значений. тир1е<ттяестет 3(1пт, 3пт, 3пт)> сети)пдоиР1асевепт(изпдоиЕ); Фиктивная функция еетЫ пдоиР)асепепт позволяет пользователям узнавать коорлинаты окна и его положение в стеке окон, используя один вызов функции. При этом разработчик библиотеки нс обязан прелусматривать отдельную структуру для кортежа, состоягнего из трех целых чисел.
Другие функции, работающие с кортежами, можно найти в файле тор1е.п библиотеки (.ой. 3. 13.3. Генерация линейных иерархий Рассмотрим следующий простой шаблонный класс, опрелеляюший интерфейс обработчика события. В нем определяется только функция-член опвчепт. теир1ате <с1ава т> с1азз ечептнапд1ег ( рыб)зс: ч3 гтца1 чоз'6 ОпЕчепт(сопат та, 1пт ечепттд) = 0; ч1гтца1 чеза -ечептнапЯегО () В классе ечептнапд1ег опрслслен виртуальный деструктор, нс прелставляюший для нас интереса, но тем нс менее необходимый (причины будут указаны в главе 4).
Шаблонный класс аепесаттегнз егагспу можно использовать для того, чтобы распространить класс Ечептнапд1ег на каждын тип в списке. туредеФ аепесаттегнзегагспу < ттреьтет 3(изпбои, вцттоп, есго11ваг), ечептнапд1ег > издоетечептнапд1ег; Часть Е Методы У класса бепвсаттегн1егагспу есть недостаток -- он использует множественное наследование. Если размер кода очень важен, этот класс может стать непригодным, поскольку класс Мддетбчептнапд1ег содержит три указателя на виртуальные таблицы', по одной для каждой конкретизации класса бчептнапд)ег.
Если величина эзхеоФ(бчептнапд)ег) равна 4 байт, то величина эзгеоР(Мддетбчептнапд1ег) будет равна уже 12 байт, возрастая по мере добавления в список новых типов. Наиболее эффективно было бы объявлять все виртуальные функции внутри класса изддетбчептнапд)ег, правда, это не позволило бы генерировать код. Линейная иерархия наследования позволяет разложить класс ызддетбчептнапд)ег на классы, по олному на каждую виртуальную функцию, как показано на рис. 3.5, При использовании простого наследования класс ызддетбчептнапд)ег может иметь только один указатель на виртуальную таблицу, что максимально повышает его эффективность с точки зрения разл1ера. Риг.
3.5. Оптимальная па размер» гтр»кт»ра класса йтдхе~ЕяатНапд)аг Как создать механизм, автоматически генерируюший такую структуру" .Для этого предназначен рекурсивный механизм, аналогичный классу бепбсаттегнзегагспу. Однако есть одно отличие. Шаблонный класс, определенный пользователем, теперь должен принимать два шаблонных параметра. Один из них является текушим типом в списке типов, как и у класса бепбсаттегнз егагс)зу, а второй — это базовый класс, от которого происходит конкретизация. Второй шаблонный параметр необходим, поскольку, как показано на рис.
3,5, код, определенный пользователем, теперь включается в середину иерархии классов, а не только в его корни (как это быдо в классе беп5саттегнз егагспу). 4 Реализация ие обязана использовать виртуальные таблицы, ио большинство программистов все же применяют нх. Описание виртуальных таблиц дано е работе (лрршап Н994). Глава Э. Списки типов Напишем рекурсивный шаблонный класс бепсз'пеагизегагс1зу. Он похож на класс вепвсассегнзегагспу, но отношение наслелования и шаблонный код, опреде- ленный пользователем, в нем обрабатываются иначе.
севр1асе < с1аьа тьззс, севр1асе <с1аав лсовзстуре,с1азз вазе> с1авв цпз'с, с1ааа лоос = вврсутуре // класс вврсутуре описан в главе 2 > с1авв вепсзпеагнз егагспу; севр)асе < с!аьв т1, с1аьа т2, севр1асе <с1азз, с1авв> с1азз цпз'с, с)азз лоос > с1азз бепьзпеагизегагспу<туре1ззс<т1, т2>, цпзс, лоос> : раб!зс цпзс< т1, аепьзпеагнзегагспу<т2, цпзс, аоот» севр1асе < с1ааа т, севр1асе <с1азз, с1азз> с1азв цпзс, с1аав яоос > с1аьв аепьзпеагнз'егагспу<тнрвпвт 1(т), цпзс, лоос> рцЫ зс цпзс<т, яоос> ( ); Этот код немного сложнее, чем у класса бепвсассегнзегагспу, но структура иерархии класса оепсз'пеагнзегагспу намного проше. Следуя пословице *'лучше один раз увидеть, чем сто раз услышать" ("ипаяе )з знои!з 1,024 зногдз"), посмотрим на рис.
3.6, на котором изображена иерархия классов, порожденная слелующим кодом. севр1асе <с1аьа т, с1ава вазе> с!азз анепснапд1ег : рыб!зс вазе ( рцЫтс: нзгсца1 нези опанепс(тй оЬ), зпс енепстд); суредеУ бепьз'пеагнз'егагспу < тнрвьтвт 3(изливов, вцссоп, всго11ваг), внепснап31 ег > мувнепснапд1ег; В сочетании с классом внепснапд1ег класс вевсьзпеагнзегагс1зу определяет линейную иерархию простого наследования. Каждый узел в этой иерархии определяет одну чисто виртуальную функцию, как это предусмотрено лля класса внепснапд1ег. Следовательно, в классе енепснапд1ег определены три виртуальные функции, как и требовалось. Класс аепьзпеагнзегагс1зу выдвигает новое требование к своему шаблонному 9а Часть й Методы параметру: класс опзс (в нашем примере — класс ееепснапб1ег) должен получать второй шаблонный параметр и наследовать его свойства.
В качестве компенсации класс бепьзпеагнз егагспу выполняет сложную работу по генерации иерархии классов. Рис. 3.6. Иерархия классов, норонсденнал шаблонным классом йенелнеагИГегагслу Классы пеп5саттегНзегагспу и ОепЬзпеагН1егагспу прекрасно работают в тандеме. В большинстве случаев интерфейс можно генерировать с помошью класса пеп5сатсегизегагспу, а реализацию — с помоШью кяасса пепсзпеагизегагспу. Конкретные примеры использования этих двух генераторов приводятся в главах 9 и 10. 3.14. Резюме Списки типов представляют собой важный метод обобшенного программирования. Они прелоставляют создателям библиотек новые возможности: выражать и манипули- Глава 3.
Списки типов ровать неограниченно большими наборами типов, генерировать структуры данных и код на основе этих коллекций и т.д. На этапе компиляции к спискам типов можно применять большинство элементарных функций, предусмотренных для обычных списков: добавлять, удалять и заменять элементы, удалять дубликаты, обращаться к элементам, осуществлять их поиск и даже выполнять их частичное упорядочение. Код, реализующий манипуляции со списками типов, ограничен чисто функциональным стилем, поскольку на этапе компиляции не существует изменяемых величин -- типы и статические константы, будучи однажды определенными, уже никогда не изменяются. По этой причине большинство операций над списками типов основано на рекурсивных шаблонах и сопоставлении образцов с помощью частичной шаблонной специализации.
Списки типов полезны, когда приходится писать один и тот же код — декларативный или императивный -- для каждого отдельного типа. Они позволяют абстрагировать и обобщать сущности, ко~орые невозможно абстрагировать и обобщать с помощью других приемов программирования. По этой причине списки типов представляют собой средство для созлания совершенно новых идиом и реализаций библиотек, как мы увидим в главах 9 и 1О. Библиотека ЕоЫ содержит два мощных примитива для автоматической генерации иерархий классов: шаблонные классы пепзсаттегизегагсйу и пепЬ1пеагн(егагс(зу. Они генерируют две основные структуры классов: распределенную (см.