|
Программирование >> Многопоточная библиотека с принципом минимализма
бой класс часто можно считать собственным суперклассом. Если проверку нужно ужесточить, можно написать следующий код. #define SUPERSUBCLASS STRICT(T, и) \ (SUPERSUBCLASS(T, U) && \ !(Conversion<const т, const и>::sameType) Зачем в этом фрагменте кода столько модификаторов const? Причина состоит в том, что проверка не должна завершаться неудачей из-за проблем, связанных с этими модификаторами. Если шаблонный код применяет модификатор const дважды (к типу, который уже является константным), второй модификатор игнорируется. Короче говоря, модификатор const в макросе SUPERSUBCLASS применяется в целях безопасности. Почему макрос назван SUPERSUBCLASS, а не base OF или INHERITS? По одной очень важной причине. Первоначально в библиотеке Loki этот макрос назывался INHERITS. Однако при использовании выражения INHERITS (т, и) каждый раз возникал вопрос, что именно проверяется: то, что класс т является производным от класса и, или наоборот? Очевидно, что выражение SUPERSUBCLASS(т, U) таких сомнений не вызывает, поскольку в его названии первая часть (SUPER) относится к первому параметру т, а вторая (SUB) - ко второму параметру и. 2.8. Оболочка вокруг класса typejnfo в стандарте язьпса С++ предусмотрен класс std: :type info, позволяющий исследовать типы объектов в ходе вьтолнения программы. Обычно класс type info применяется в сочетании с оператором typeid, возвращающим ссылку на объект класса type info. void Fun(Base* pObj) { Сравнивает два объекта типа type info с типами *pObj и Derived, соответственно if (typeid(*pObj) == typeid(Derived) { ... ага, на самом-то деле указатель pObj ссылается на объект класса Derived ... } Оператор typeid возвращает ссылку на объект класса type info. Кроме операторов сравнения operator== и operator !=, класс type info содержит еще две функции. Функция-член name возвращает текстуальное представление типа в форме переменной типа const char*. Стандартного способа преобразовывать имена классов в строки нет. Поэтому не следует ожидать, что значением выражения typeid (widget) будет строка widget . Вполне приемлемо (хотя и не слишком хорошо), если реализация функции-члена type info: :name возвратит для всех типов пустую строку. Функция-член before устанавливает между объектами типа type info отношение порядка. Используя эту функцию, можно индексировать объекты класса type info. К сожалению, полезные свойства класса type info реализованы так, что их трудно эксплуатировать. В классе type info не предусмотрены конструктор копирования и оператор присваивания, что не позволяет хранить в памяти объекты этого типа. Однако можно хранить указатели на объекты типа type info. Объекты, возвращаемые оператором typeid, хранятся в статической памяти, поэтому беспокоиться о времени их жизни не стоит. Вместо этого следует обеспечить идентичность указателей (pointer identity). Стандарт языка С++ не гарантирует, что при каждом вызове, например, оператора typeid ( int), возвращается ссылка на один и тот же объект класса type info. Следовательно, сравнить указатели на объекты класса type info невозможно. Хранить указатели на объекты этого типа и сравнивать их между собой нужно с помощью функции type info: :operator==, которая применяется к разыменованным указателям. При необходимости рассортировать объекты класса type i nfо снова нужно сохранить указатели на них, но на этот раз использовать функцию-член before. Следовательно, для того чтобы применить упорядоченные контейнеры из библиотеки щаблонов STL, нужно написать небольшой функтор (functor) и поработать с указателями. Все это довольно неудобно. Для того чтобы преодолеть эти трудности, создадим вокруг Kjiacca type i nf о интерфейсный класс (wrapper class), в котором хранится указатель на объект типа type i nf о и предусмотрено следующее. Все функции-члены класса type i nf о. Семантика значений (открытый конструктор копирования и оператор присваивания). Операторы сравнения operator< и operator==. В библиотеке Loki определен интерфейсный класс Typelnfo, в котором реализована описанная выше оболочка класса type i nf о. Вот его краткий обзор. class Typelnfo { public: Конструкторы/деструкторы Typeinfo(); Необходим для контейнеров Typelnfo(const std::type info&); Typelnfo(const Typeinfo&); Typelnfo& operator==(const TypeinfoA); функции сравнения bool before(const Typelnfo&) const; const char* nameO const; private: const std::type info* plnfo ; Операторы сравнения bool operator==(const Typelnfo&, const TypelnfoA); bool operator!=(const Typelnfo&, const TypeInfo&); bool operator<(const TypeinfoA, const Typeinfo&); bool operator<=(const Typeinfo&, const Typeinfo&); bool operator>(const TypelnfoA, const TypelnfoA); bool operator>=(const Typelnfo&, const Typeinfo&); Благодаря конструктору преобразования (conversion constructor), получающему в качестве параметра объект класса std: : type i nfo, можно непосредственно сравнивать объекты типов Typelnfo и std: :type info, как показано ниже. void Fun(Base* pObj) { Typelnfo info = typeid(Derived); if (typeid(*pObj) == info) ... Указатель pBase действительно указывает 60 Часть I. Методы на объект класса Derived ... Способность копировать и сравнивать между собой объекты класса Typelnfo важна во многих ситуациях. Фабрики клонирования, описанные в главе 8, и механизм двойной диспетчеризации, рассмотренный в главе И, достаточно ярко иллюстрируют этот факт. 2.9. Классы NullType и EmptyType в библиотеке Loki определены два очень простых типа: NullType и EmptyType. Их можно использовать для идентификации более широких типов. Класс NullType служит в качестве нулевого маркера типов (null marker), class NullTypeO; Обычно объекты этого класса не создаются. Его единственное предназначение - обозначить типы, не представляющие интереса. В разделе 2.10 класс NullType используется в ситуациях, когда тип имеет синтаксический смысл, но не имеет семантического. (Например: На объекты какого типа указывает переменная типа int? .) Кроме того, списки типов, описанные в главе 3, используют класс NullType в качестве маркера конца списка и для возврата сообщения тип не найден . Второй вспомогательный тип - класс EmptyType. Как и следовало ожидать, его определение имеет следующий вид. struct EmptyType {}; Этот тип можно использовать в качестве базового класса, а также для передачи значений типа EmptyType. Кроме того, его можно применять в шаблонах в качестве типа, заданного по умолчанию ( не важно какой ). 2.10. Характеристики типов Характеристики (traits) - это метод обобщенного профаммирования, позволяющий принимать решения на этапе компиляции профаммы, основываясь на информации о типах, аналогично тому, как в ходе выполнения профаммы принимаются рещения, основанные на значениях (Alexandrescu, 2000а). Предоставляя дополнительный окольный путь , позволяющий решить многие проблемы, связанные с проектированием профаммного обеспечения, характеристики дают возможность принимать решения, основываясь на информации о типах, находясь вне конкретного контекста. Код, полученный в результате, становится яснее, читабельнее и легче в эксплуатации. Обычно профаммисты пишут свои собственные характеристические шаблоны и классы для обобщенного кода. Однако характеристики можно применять к любым типам. Они помогают программисту, создающему обобщенный код, точнее согласовать шаблонный код с возможностями типа. Допустим, что мы реализуем алгоритм копирования. template <typename Init, typename Outit> Outit CopyCinit first, init last, Outit result) for (; first != last; ++first, ++result) result = *first;
|
© 2006 - 2024 pmbk.ru. Генерация страницы: 0
При копировании материалов приветствуются ссылки. |