Скачать презентацию
Идет загрузка презентации. Пожалуйста, подождите
Презентация была опубликована 9 лет назад пользователемВалерия Лундышева
1 Лекция 23. Шаблоны (часть 3) Красс Александр СПбГУ ИТМО, 2008
2 Темы Частичная специализация Неполная специализация Шаблонные члены класса Шаблоны как параметры шаблона Функциональные объекты Traits
3 Частичная специализация Нам уже знакома полная специализация: template class vector { … }; template<> class vector { …}; Частичная специализация производится для подтипа: template class vector { }; // специализация вектора для указателей. vector может проходить по своему содержимому и вызывать delete для каждого элемента. STL такой специализации нет.
4 Частичная специализация (2) А для чего еще можно выполнять частичную специализацию? const TКонстантные типы T*Указатели T&Ссылки T[integer-const]Массивы Type(*)(T)Указатель на функцию с параметром типа T T(*)()Указатель на функцию, возвращающую T T(*)(T)Указатель на функцию с параметром типа T и возвращающую T
5 Неполная специализация Неполная специализация позволяет нам указать часть параметров шаблона. template class Map {…}; Вариант Map для случая, когда ключ задается целым: template class Map {…}; Неполная специализация не нужна для шаблонных функций, т.к. там можно обойтись перегрузкой.
6 Шаблонные члены класса Иногда имеет смысл делать шаблонными не весь класс, а отдельные методы. Рассмотрим шаблонный класс Array: template class Array { … }; Array это динамический массив в стиле C++. Когда ему нужно перераспределить память, он вызывает оператор new, а когда удалить, оператор delete. Очевидно, что для разных случаев нужны разные стратегии управления памятью. (Пояснить?)
7 Шаблонные члены класса (2) Вынесем стратегию управления памятью в отдельный класс: class StdMemoryMgr { public: template static T* alloc(int n) { return new T[n]; } template static void free(T *p) { delete[] p; } }; Реализуя разные версии этого класса, мы поддерживаем разные стратегии управления памятью. Например, вот так: class NoFreeMemoryMgr { public: template static T* alloc(int n) { return new T[n]; } template static void free(T *p) {} }; Класс NoFreeMemoryMgr можно использовать, если удаление очень дорого и оно нам не критично,
8 Шаблонные члены класса (3) Теперь наш класс Array будет иметь следующий вид: template class Array { … }; Выделение памяти в классе будет производиться так: T* buf = Allocator::alloc (size); Освобождение памяти так: Allocator::free (buf);
9 Шаблоны как параметры шаблона Аргументами шаблона могут быть не только простые типы, но и типы основанные на шаблонах К примеру, рассмотрим структуру данных стек. У него есть следующие операции: –Push – помещаем элемент в стек –Pop – удаляем элемент из стека –Size – количество элементов в стеке Стек может быть реализован или на базе списка или на базе массива. Если мы автора класса Stack, мы не можем за пользователя решать, какую структуру нам использовать. Вынесем структуру в параметр шаблона template > class Stack { void Push(const Type &v) { impl.push_back (v); } void Size() const { impl.size(); } … private: Container impl; }; Использование: 1. Stack si1; // стек на базе структуры данных по умолчанию (массива) 2. Stack > si2 ; // стек на базе списке. Обратите внимание на пробел между знаками >.
10
Функциональные объекты Рассмотрим функцию find1, которая ищет указанное число в массиве типа int: const int* find1 ( const int* pool, int n, int x ) { const int* p = pool; for ( int i = 0; i
11
Функциональные объекты (2) Обобщим нашу функцию find, добавив возможность поиска элемента по произвольному условию: const int* find2 ( const int* pool, int n, bool (*cond)(int) ) { const int* p = pool; for ( int i = 0; i
12 Функциональные объекты (3) В итоге для реализации обобщенного поведения некоторой функции нам надо передать в нее callback, который и будет выполнять настройку поведения этой функции Этот подход имеет и достоинства и недостатки: –Механизм очень гибкий –Низкая скорость из-за лишних вызовов функций. (Функции по указателю нельзя встроить) Хорошо бы сохранить гибкость callback и увеличить скорость. Воспользуемся помощью классов и шаблонов.
13 Функциональные объекты (4) Тип с определенным оператором вызова функции называется функциональным типом. Функциональные типы делятся на встроенные и определяемые пользователем –Указатель на функцию – это встроенный тип –Класс с оператором вызова функции – определяемый пользователем
14
Функциональные объекты (5) После замены указателей на функциональные объекты и приведения функции к шаблонному виду получим: template T* find3 ( T* pool, int n, Comparator comp ) { T* p = pool; for ( int i = 0; i
15 Функциональные объекты (6) Теперь мы можем использовать find3 следующим образом: 1. Определим предикат поиска: template class less { public: bool operator()(T x) const { return x < N; } }; 2. Используем: int *p = find3(A, 100, less ()); 3. В STL есть аналогичная функция std::find и набор аналогичных предикатов на все случаи жизни.
16 Traits Предположим, нам надо написать набор математических функций, работающих с разными типами данных: float, double, long double, плюс типы, определяемые пользователем. При реализации этих функций нам хотелось бы знать о свойствах этих типов: –Максимальное и минимальное значение –Точность –И пр. Реализация этих функций должна быть максимально обобщенной. Следовательно, используем шаблоны:
17 Traits (2) Все эти величины для стандартных типов определены в файле float.h: /* Smallest value such that 1.0+xxx_EPSILON != 1.0 */ #define DBL_EPSILON e-016 #define FLT_EPSILON e-07F #define LDBL_EPSILON e-019L /* max value */ #define DBL_MAX e+308 #define FLT_MAX e+38F #define LDBL_MAX e+4932L Но как организовать доступ к этим величинам из наших функций, в зависимости от типа аргументов? template bool IsZero(T val) { return abs(val) < 10 * eps; } // eps – своя для каждого типа. Решение заключается в использовании Traits-ов
18 Traits (3) 1. Определим обобщенный trait для всех типов: template class float_attrs { /* Пусто. Мы ничего не можем сказать про неизвестный тип. */}; template class float_attrs // для double { public: typedef double float_type; static inline float_type epsilon() { return DBL_EPSILON; }... }; 2. Теперь определим специализацию этого класса для различных типов: template struct float_attrs // для float { public: typedef float float_type; static inline float_type epsilon() { return FLT_EPSILON; }... };
19 Traits (4) Функция IsZero: template bool IsZero(T val) { return abs(val) ::epsilon(); } Использование: IsZero(1.f); // используем float_attrs float_attrs это и есть трейд с, хранящий атрибуты чисел с плавающей запятой. Другое применение traits-ов это реализация стратегий, изменяющих поведение объекта.
20 20 Спасибо за внимание Вопросы?
Еще похожие презентации в нашем архиве:
© 2024 MyShared Inc.
All rights reserved.