|
Программирование >> Разработка устойчивых систем
Шаблоны Шаблоны С++ представляют собой нечто гораздо большее, чем контейнеры для Т . Хотя первоначально шаблоны проектировались именно как основа для создания обобшенных контейнеров, безопасных по отношению к типам, в современном языке С++ шаблоны также используются для автоматического построения специализированного кода и оптимизации выполнения программ. В этой главе мы с практической точки зрения рассмотрим возможности шаблонов в современном языке С++ (а также типичные ошибки, допускаемые программистами при их использовании). Параметры шаблонов Как отмечалось в первом томе, шаблоны делятся на две категории: шаблоны функций и шаблоны классов. Обе разновидности шаблонов полностью характеризуются своими параметрами. Параметры шаблонов могут представлять: типы (встроенные или пользовательские); константы времени компиляции (например, целые числа, указатели или ссылки на статические данные; часто называются нетиповыми параметрами); другие шаблоны. Все шаблоны, упоминавшиеся в первом томе, относятся к первой, наиболее распространенной категории. Классическим примером простого контейнерного шаблона является стек (Stack). Для объекта Stack как контейнера неважно, объекты какого типа в нем хранятся; логика его работы не зависит от типа элементов. По этой причине тип элементов может быть параметризован: tempiate<class Т> class Stack ( Т* data: size t count: Нетиповые параметры шаблонов Нетиповой параметр щаблона должен быть целочисленным значением, известным на стадии компиляции. Например, для создания стека фиксированного размера можно использовать нетиповой параметр, который будет определять размер базового массива: tempiate<class Т. size t N> class Stack { T data[N]: Фиксированная емкость N size t count: public: void pushCconst T& t): И т. д. При специализации щаблона в нетиповом параметре N необходимо передать константу, известную на стадии компиляции, например: Stack<int. 100> myFixedStack: Поскольку значение N известно на стадии компиляции, базовый массив (data) может храниться не в куче, а в стеке. Это позволяет повысить быстродействие программы, так как избавляет от издержек, связанных с динамическим выделением памяти. По аналогии с предыдущим примером, специализации будет присвоено имя Stack<int,100>. А это означает, что разные значения N порождают уникальные типы классов. Например, класс Stack<int,99> отличен от класса Stack<intlOO>. Из всех классов стандартной библиотеки С++ только щаблон bitset использует нетиповой параметр, определяющий количество битов в объекте (шаблон bitset подробно рассматривается в главе 7). В следующем примере генератор случайных чисел задействует объект bitset для отслеживания сгенерированных чисел, чтобы числа начинали повторяться только после полного перебора всего интервала. Обратите внимание на перегрузку оператора (), обеспечивающую привычный синтаксис вызова функции. : C05:Urand.h {-Ьог} II Генератор уникальных случайных чисел #ifndef URAND H #define URAND H #i nclude <bitset> #i nclude <cstddef> #include <cstdlib> #1 nclude <ctime> using std::size t: public: void pushCconst Т& t): И т. д. Фактический тип, используемый конкретным экземпляром Stack, определяется аргументом, передаваемым в параметре Т: Stack<int> myStack: Контейнер Stack с элементами типа int Компилятор создает версию Stack для типа int, для чего он подставляет int вместо Т и генерирует соответствующий код. В данном случае будет сгенерирован спе-циа.;1изированный класс с именем Stack<int>. using std::bitset: tempiate<size t UpperBound> class Urand { bitset<UpperBound> used; public: UrandO { srand(time(0)): } Раскрутка size t operatorO0; Функция-генератор tempiate<size t UpperBound> inline size t Urand<UpperBound>::operator()() { if(used.countО == UpperBound) used.resetO: Сброс (очистка объекта bitset) size t newval: whi1e(usedCnewval = randO % UpperBound]) : Пока не будет найдено уникальное значение usedСnewval] = true: return newval; #endif URAND H /:- Программа Urand генерирует только случайные числа без повторений, поскольку объект bitset с именем used отслеживает все возможные числа в интервале (верхняя граница задается аргументом шаблона) и запоминает каждое число, устанавливая бит в соответствующей позиции. После того как все числа будут использованы, объект bitset сбрасывается, и все начинается сначала. По следующей простой тестовой программе можно понять, как работать с объектом Urand: : C05:UrandTest.cpp {-bor} linclude <iostream> linclude Urand.h using namespace std: int mainO { Urand<10> u; fordnt 1 - 0: i < 20; ++i) cout uO * ; } 111:- Как будет показано в этой главе, нетиповые аргументы шаблонов также играют важную роль при оптимизации математических вычислений. Аргументы шаблонов по умолчанию Аргументы шаблонов могут иметь значения по умолчанию в шаблонах классов, но не в шаблонах функций. Как и обычные аргументы функций по умолчанию, они должны определяться только один раз, когда компилятор впервые встречает объявление или определение шаблона. Если одному из параметров шаблона был назначен аргумент по умолчанию, они та1чже должны быть назначены всем аргументам, следующим за ним. Например, чтобы сделать шаблон стека фиксированного размера чуть более удобным, можно добавить определение аргумента по умолчанию: tempiate<c1 ass Т. size t N = 100> class Stack ( Т data[N]; Фиксированная емкость N size t count:
|
© 2006 - 2024 pmbk.ru. Генерация страницы: 0
При копировании материалов приветствуются ссылки. |