Д. Вандевурд, Н.М. Джосаттис - Шаблоны C++. Справочник разработчика (2003) (1160769), страница 59
Текст из файла (страница 59)
16.2.2. Члены как базовые классы Оптимизация пустого базового класса не имеет эквивалента для данных-членов, поскольку, помимо прочего, это создало бы ряд проблем с представлением указателей иа члены. В результате иногда то, что реализовано как данные-члены, желательно реализовать в вале (закрытого) базового класса. Однако и здесь не обходится без проблем.
Наиболее интересна эта задача в контексте шаблонов, поскольку параметры шаблона часто заменяются типами пустых классов (хотя, конечно, полагаться иа это нельзя). Если о параметре типа шаблона ничего не известно, оптимизацию пустого базового класса осуществить не так-то легко. Рассмотрим тривиальный пример. севр1асе <сурепаве Т1, сурепава Т2> с1авв МуС1авв рктчаке: т1 а; Т2 Ь| Вполне возможно, что один или оба параметра шаблона заменяются типом пустот~ класса. В этом случае представление МуС1авв<Т1,Т2> может оказаться не оптимальным, что приведет к напрасной трате одного слова памяти для каждого экземпля рамуС1авв<т1,т2>.
Этого можно избежать, сделав аргументы шаблона базовыми классами. севр1аке <Сурепаве Т1, Сурепаве Т2> с1авв МуС1авв : ргтчасе Т1, ргтчасе Т2 1 16.2. Оптимизация пустого базового класса 319 хешр1ахе <хурепалю СизсошС1азз> с1азз ОрххшхяаЫе ( рх1чахе: СизхошС1азз хпбо; чоЫ* зхогадез // Может быть пустым можно записать: хешр1ахе <Гурепаше СизхошС1азз> с1азз ОрххшххаЫе ( ргхчаге: ВазеМешЬеграхг<СизсошС1азз, чохб"> 1пбо апс( зсогаде; Даже беглого взгляда достаточно, чтобы понать, что использование шаблона ВазеМешЬеграхг делает реализацию ОрсхшххаЫе более многословной. Однако некоторые разработчики библиотек шаблонов отмечают, что повышение производительности (для клиентов их библиотек) стоит этой дополнительной сложности.
Реализация ВаземешЬеграхг может быть довольно компактной. // хпЬегхх/ЬазешешЬеграхг.Ьрр «Нпдей ВАЯВ МЕМВЕК РА1К НРР «х(ей«пе ВАНЕ МЕМВЕК РА1~ НРР хешр1ахе <хурепаше Вазе, хурепаше мешьех> с1азз ВаземешЬегРахг : рг1чахе Вазе ( рг1чаге: МешЬег хпешЬег; Однако этот простой вариант имеет свои проблемы. Он не сработает, если т1 нли т2 заменяются типом, не являющимся классом или типом объединения. Он также не работает, если два параметра заменяются одним и тем же типом (хотя можно легко решить эту проблему добавлением лишнего уровня наследования, как было показано ранее в главе). Но даже после решения этих проблем адресации останется еще одна очень серьезная проблема: добавление базового класса может существенно изменить интерфейс данного класса.
Для нашего кпасса МуС1азз зта проблема может показаться не очень значительной, поскольку здесь совсем немного взаимодействующих элементов интерфейса, но, как будет показано далее в главе, наличие виртуальных функций-членов меняет картину. Понятно, что рассматриваемый подход к ЕВСО чреват всеми описанными валами проблем. Более практичное решение может быть изобретено для распространенного случая, когда параметр шаблона заменяется только типами классов и когда доступен другой член шаблона класса. Основная идея состоит в том, чтобы "слить" потенциально пустой параметр типа с другим членом с использованием ЕВСО.
Например, вместо записи Глава 16. Шаблоны и наследование 320 риЫ1с: // Конструктор ВавемепЬеграйг (Вазе сопвсй Ь, мегвЬег сопвсй гв) Вазе(Ы, гвемЬег (га) ( ) // Доступ к данным базового класса через й1гвг() Вазе сопвсй й1гвг() сопвс ( гегигп (Вазе соплей)*ГЬйв; Ваней Ййгве() ( геспгп (Ваней)*ГЫв; ) // Доступ к члену-данным посредством весопг)() МегвЬег соплей весопг)!) сопят ( гесигп сЬйв->лгепЬег; МегвЬегй весопс)() ( гесигп гЬйв->шежЬегг ) 4епдйй // ВАЯЕ МЕМЕД РА1К НРР Для доступа к инкапсулированным (и, возможно, оптимизированным с точки зрения расхода памяти) элементам данных реализация должна использовать функции-члены Е1гвс() и весопг)() . 16.3.
Модель необычного рекуррентного шаблона Это странное название (сппопз1у гесшт1пй гешр1аге рапегп — СкТР) обозначает общий класс методов, которые состоят в передаче класса-наследника в качестве аргумента шаблона одному из собственных базовых классов. В самой простой форме код С++ та кой модели выглядит, как показано ниже. сегвр1аге <гурепагае пегйуес)> с1авв СцгйопвВаяе ( с1авв Сигйоиз: риЫ1с СпгйоивВаве<Сигйоив> ( 16.3.
Модель необычного рекуррентного шаблона 321 Эта первая схема СУТР имеет независимый от параметра шаблона базовый класс: Сиг1оив не является шаблоном и, следовательно, защищен от проблем видимости имен зависимых базовых классов. Однако это не главная характеристика СКТР. Действительно, точно так же можно было использовать альтернативную схему. гешр1аге <гурепаше бегучее(> с1авв Сиг1оивВаве ( Сешр1асе <Гурепатпе Т> с1авв СигйоивТешр1асе: риЫйс СигйоивВаве<Сигйоивтешр1асе<Т» ( От этой схемы недалеко до еше одной альтернативы, на этот раз включающей шаблон- ный параметр шаблона. Гетр1асе <Гешр1асе<турелей> с1авв Оег1чед> с1авв МогеСигйоивВаве ( Гетр1аее <Гурепаше Т> с1авв МогеСигйоив : риЫТс МогеСигйоивВаве<МогеСигйоив> ( Простейшее применение СУТР— отслеживание количества созданных объектов некоторого типа класса.
Этого легко достичь посредством увеличения целого статического члена-данных в каждом конструкторе и его уменьшения в деструкторе: Однако необходимость обеспечить соответствующий код в каждом классе весьма утомительна. Вместо этого можно написать шаблон, приведенный ниже. // Тппегйс/оЬ3есссоипеег.прр 11пс1ис(е <вес(бей.Ь> сетр1аее <гурепаше соипгебтуре> с1авв ОЬЗессСоипсег ( рг1часе: вгагйс вйяе г соиле; // Количество объектов ргогесгед: // Конструктор по умолчанию ОЬбесГСоипсег(1 ++Оьбесссоипгег<соипгес(туре>::соиле; Глава (б. Шаблоны и наследование 322 // Конструктор копирования ОЬЗесгСоцпгег(ОЬЗесГСоцпсег<Соцпбег(Туре> сопзсй) ( ++ОЬ5есГСоипсег<Соцпгейтуре>::соцпс/ ) // Деструктор -ОЬтессСоцпгег() ( --ОЬ2ессСоцпсег<Соцпгег(Туре>::соцпг; ) риЬ11с: // Возвращение количества имеющихся объектов: згагйс з(ге с 11че() ( гегигп ОЬЗесГСоцпсег<СоцпседТуре>::соцпс; ) // Инициализация счетчика значением ноль сещр1асе <гурепаще соцпсейтуре> вйхе с ОЬЗесгСоцпгег<Соцпгес(Туре>::соцпс = О; Если вы хотите подсчитать количество активных (не уничтоженных) объектов некоторого типа класса, для зтого достаточно породить класс из шаблона ОЬз есГСоцпсег.
Например, можно определить и использовать класс строк с подсчетом объектов. // ТпЬег1с/гезссоцпсег.срр ()Тпс1цс)е "оЬОесссоцпсег.Ьрр" ((Тпс1иг)е <Тозсгеащ> Гещр1асе <сурепаще СЬагТ> с1азв МуЯггйпд : рцЬ11с ОЬ2ессСоипсег<МуЯсгТпд<СЬагТ» ( Тпе пайп() ( МуЯСг1пд<сЬаг> в1, з2; МуЯГг1пд<исЬаг Г> ыз; вес(::соцс « "пшаЬег об Муясгйпд<сЬаг>: « МуЯГгйпд<сЬаг>:: 11че() « зЫ::епс)1; зсс)::соис « "пцщЬег ой Муясгйпд<исЬаг с>: « ыз.13лте() « вЫ::епс)1( В общем случае метод СГ<ТР полезен для отделения реализаций интерфейсов, кото рые могут только быть функциями-членами (например, конструкторы, деструкторы ил" операторы индексации). 16.4.
Параметризованная виртуальность 323 16.4. Параметризованная виртуальность Язык С++ позволяет непосредственно использовать для параметризации шаблонов три вида объектов: типы, константы и шаблоны. Однако косвенно он позволяет параметризовать и кое-что другое, например виртуальность функций-членов. Простой пример демонстрирует эту удивительную методику.
// 1пЬег1г/ч1гсиа1.срр $1пс1ийе <1озсгеаш> с1азв ИосЧ1гсиа1 ( ): с1авв Ч1гсиа1 ( риЫ1с: ч1ггиа1 чо1й Еоо() ( ) Гетр1асе <сурепке Чваве> с1авз Вазе : рг1чаге ЧВазе риЫ1с: // Виртуальность йоо() зависит от его объявления // (если таковое имеется) в базовом классе ЧВаве чо1й йоо() ( зсй::соис « "Вазе:ьбоо()" « '1п'; сепр1аее <Куропатке Ч> с1азв Вег1чей : риЫ1с Ваве<Ч> ( риЫ1с: чо1й Еоо() ( всй::соиг « "эег1чей: >боо()" « '~п'; ) 1пс льв1п() ( Ваве<ыогЧ1ГГиа1>* р1 = пеы Рег1чей<ыоГЧ1ггиа1>; р1->йоо(); // Вызов Вазе::йоо() Ваве<чйггиа1>* р2 = пеы рег1чей<Ч1гсиа1>/ р2->йоо()! // Вызов Вег1чей:ьбоо() Глава 16.Шаблоны и наследование 324 Этот метод предоставляет инструмент для разработки шаблона класса, который пригоден для использования как при инстанцировании конкретных классов, так и при расширении с применением наследования.
Однако только этого далеко не всегда достаточно для того, чтобы добавить виртуальность для некоторых функций-членов в целях получения более специализированного базового класса. Этот метод разработки требует использования фундаментальных конструкторских решений, поэтому обычно более практичной является разработка двух различных инструментальных средств (класса или иерархии шаблонов классов), чем попытка интегрировать их в одну шаблонную иерархию. 16.5. Заключение Именованные аргументы шаблона используются для упрощения некоторых шаблонов классов в библиотеке Воозп Эта библиотека использует метапрограммирование для создания типа со свойствами, подобными нашему шаблону Ро1зсупе1есгог (но без виртуального наследования).