
Параметрический полиморфизм
План
1. Обобщенная идея.
2. Шаблоны функций. Классический С++.
3. Шаблоны классов. Классический С++.
4. Родовые объекты. Управляемый С++/CLI.
5. Примеры типов – шаблонов в Net.Framework.
1. Обобщенная идея
Много алгоритмов, которые применимы к разным типам данных.
Перегрузка функций – решение проблем.
Введение
Функция или тип являются параметризованными, когда один или несколько типов, используемых в определении, являются неопределенными. Используя эту функцию или тип, пользователь может по своему усмотрению подставлять требуемые типы данных при обращении к функции или порождении объекта класса.
Пример
class My_Object
{
...
}
void Swap (int &x, int &y)
{
int buf = x;
x = y;
y = buf;
}
void Swap (My_Object &x, My_Object &y)
{
My_Object buf = x;
x = y;
y = buf;
}
Эти функции отличаются только типом параметров, который можно объявить обобщенным именем, например:
void Swap (Type &x, Type &y)
{
Type buf = x;
x = y;
y = buf;
}
Параметром является имя типа, названное обобщенным именем Type – имя любого типа.
При обращении к функции на место параметров с указанным именем можно передавать данные произвольных типов, в том числе, базовых, или конструируемых, в том числе классов.
Ограничения для производных типов.
К параметризованным типам относятся
1. Шаблоны, унаследованные от С++.
2. Родовые (общие) объекты, которые являются параметризованными типами CLI/C++.
1.1. Шаблоны функций
Позволяют создавать процедуры, которые могут одинаковым образом обрабатывать данные разных типов.
Примеры: поиск, сортировка, суммирование и похожие алгоритмы, как правило, применяются к последовательности (совокупности, коллекции) данных.
Пример. Поиск
а) в одномерном массиве целых чисел.
Для поиска необходимо выполнять операцию сравнения a[i] с образцом.
a[i]==Целое_число
б) в массиве строк
Для поиска необходимо выполнять операцию сравнения строки с образцом.
cmpstr(строка_1, строка_2)
в) в массиве записей
Для поиска необходимо выполнять операцию сравнения данного произвольного типа с образцом.
Сидоров Сидор 180 90
Петров Петр 200 100
Козлов Казимир 180 60
1) по фамилии
2) по имени
3) по фамилии и имени
4) по прочим данным
Пример попытки использования обобщенной функции – функция сортировки элементов массива.
Qsort (указатель_на_массив, число_эл-тов, размер_элемента,
указатель_на_функцию_сравнения_элементов);
Дополнительно к имени массива функция должна получить параметры:
1) размер_элемента;
2) указатель_на_функцию_сравнения_элементов. Такая функция должна уметь сравнить данные передаваемого типа и вернуть логическое значение, потому что в любом алгоритме сортировки необходимо сравнить данные и переместить их.
int Relation ()
{
return a[i]<образец;
}
int Relation ()
{
return cmpstr(a[i],образец);
}
int < int
float < float
char * < * char
Объект_1 < Объект_2
и так далее.
Это неудобно, хотя возможно.
В отличие от механизма перегрузки функций, где функция переопределяется столько раз, сколько необходимо сигнатур параметров, при использовании шаблона функции одно объявление функции порождает семейство функций.
Можно отнести к параметрам (параметризировать):
1) тип возвращаемого значения;
2) типы параметров функции (при фиксированных числе и порядке);
3) локальные данные функции.
Синтаксис шаблона функции (присутствует ключевое слово template)
template <список_параметров_шаблона> тип_функции имя_функции (параметры_с_указанием_типа)
{
тело - определение функции;
}
Сравнить с обычным описанием функции.
Что можно параметризировать?
1. Тип данных.
2. Тип функции.
Важно: Сигнатура параметров одинакова.
Требования
1. Список формальных параметров шаблона указан в угловых скобках < >, и это обобщенное имя типа данного.
2. Каждый формальный параметр шаблона обозначается служебным словом class, за которым следует имя параметра (абстрактное имя типа).
3. В теле шаблона определяется функция, в которой типы параметров и тип возвращаемого значения обозначаются именами параметров шаблона. Они же в теле функции могут обозначать типы локальных объектов.
Пример - шаблон функций Swap для обмена двух значений.
template <class Type> void Swap (Type &x, Type &y)
{
Type buf = x;
x = y;
y = buf;
}
Здесь Type - параметр шаблона функции, используется:
а) в заголовке для спецификации формальных параметров;
б) в теле шаблона для объявления локальной переменной;
в) может объявлять тип функции.
Механизм шаблона функции
Компилятор обрабатывает вызовы функций. Он знает тип данных и значения при обращении, соответственно, генерирует код функции нужного типа, заменяя абстрактное (параметризованное) имя Type реальным именем типа.
Например, если в программе объявлены:
float a,b;
a=2.3;
b=7.9;
, то для обращения
Swap(a,b);
будет генерирован код:
void Swap (float & x, float & y)
{
float z = x;
x = y;
y = z;
}
, и далее выполнено обращение именно к этой функции.
Для переменных другого типа генерируется другой код функции.
Замечание
Для обычных данных использование шаблона не имеет особенностей.
Для объектов класса есть особенности:
конструктор копирования копирует не объект, а его адрес (поверхностное копирование);
другие операции могут не быть поняты компилятором, поэтому в большинстве случаев операции следует перегружать.
Примеры в консольном Template.
1. Шаблон функции Swap.
Для простых типов.
Для строк.
Для объектов класса.
2. Пример шаблонов функций обработки массива для поиска максимума в массивах разных типов.
Для простых типов.
Для объектов класса.