- •Введение. Принципы объектно-ориентированного программирования
- •Глава 1. Классы и объекты
- •1.1. Операция разрешения области видимости ::
- •1.2. Перечислимый тип
- •1.3. Модификатор const
- •1.4. Новый тип данных – ссылка &
- •Inline определение_функции
- •2. Определение класса. Сокрытие информации.
- •3. Объект.
- •4. Конструкторы и деструкторы
- •4.1.Назначение конструктора
- •4.2. Конструктор копирования
- •X::X(X&); // где X – имя класса
- •4.3. Деструктор
- •5. Неявный указатель this
- •6. Перегрузка операций
- •7. Примеры перегрузки некоторых операций
- •7.1. Перегрузка операции [ ]
- •7.2. Перегрузка операции ()
- •7.6. Перегрузка операции (тип)
- •8. Дружественность
- •Istream
- •10. Массивы объектов.
- •11. Функции- и классы-шаблоны
- •11.1 Функции-шаблоны (родовые функции)
- •11.2 Классы-шаблоны
- •12. Член-данные класса – объекты другого класса: агрегированные классы.
- •Глава 2. Наследование. Полиморфизм
- •1. Базовый и порожденный классы
- •2. Конструкторы порожденного класса
- •3. Стандартные преобразования при наследовании
- •4. Множественное наследование. Виртуальный базовый класс
- •4.1. Прямые базовые классы
- •4.2. Виртуальный базовый класс
- •5. Полиморфизм, раннее и позднее связывание, виртуальные функции
- •5.1 Раннее (статическое) и позднее (динамическое) связывание
- •5.2. Определение виртуальной функции
- •5.3. Чистая виртуальная функция и абстрактный класс
- •5.4. Правила определения виртуальных функций
- •5.5. Механизм позднего связывания
- •6. Библиотека fstream – работа с файлами
- •Глава 3. Библиотека стандартных шаблонов (бсш). Контейнеры
- •1. Контейнер. Структура бсш.
- •2. Контейнер Vector – динамический массив
- •Контейнер list – список
- •4. Контейнер Set – множество
- •Содержание
- •Глава 1. Классы и объекты
- •Глава 2. Наследование. Полиморфизм
- •Глава 3. Библиотека стандартных шаблонов (бсш). Контейнеры
11. Функции- и классы-шаблоны
11.1 Функции-шаблоны (родовые функции)
Определение. Если функции выполняют одинаковые действия, но над данными разных типов, то можно определить одну функцию-шаблон.
Определение имеет формат
template <class X[, class Y,…]> имя_функции
{определение_функции}
Причем вместо типа какого-либо аргумента (или всех) или возвращаемого значения может стоять X, Y,…, где X, Y,… – идентификаторы для обозначения произвольного типа данных.
Рассмотрим такую задачу: отсортировать массив целых чисел и строк методом пузырька.
Вообще говоря, потребовалось бы создать 2 функции для сортировки каждого массива и 2 для обмена двух значений, т.е. 4 функции. Вместо этого напишем 2 функции-шаблона. Определим дополнительно еще 2 функции шаблона – для нахождения max элемента в массиве данных и вывода на экран двух значений данных разного типа.
enum boolean {FALSE, TRUE};
#include "string.hpp"
#include "complex.hpp"
template <class T> inline void Swap(T & a, T & b)
{T c;
c = a, a = b, b = c;
}
template <class T> T Max(T * a, int n)
{int i;T max = a[0];
for(i = 1; i < n; i++)
if (a[i] > max) max = a[i];
return max;
}
template <class T> void Bul_bul(T * a, int n) // улучшенный «пузырек» с флагом f
{int i, j, f;
for(i = 0, f = 1; i < n – 1 && f; i++)
for(j = f = 0; j < n – i – 1; j++)
if (a[j] > a[j + 1]) {Swap(a[j], a[j + 1]); f = 1;}
}
template <class X, class Y> void out2(X a, Y b)
{cout << a << ' ' << b; }
Как же будет работать компилятор?
При вызове функций-шаблонов в зависимости от типов фактических аргументов компилятор создает различные версии каждой из этих функций. Говорят – компилятор создает порожденную функцию. Процесс генерации порожденной функции называют созданием экземпляра функции. Таким образом, порожденная функция – это конкретный экземпляр функции-шаблона.
void main()
{ Complex b[5] = {Complex(4, 2), Complex(-3, 4), 1, -2};
String s[5] = {"Петров", "Сибирякова", "Горцев", "Савин", "Буторина"};
int a[10] = {5, 3, 9, 6, 2, 0, 1152, 7, 4}, i;
cout << "\n Мax среди строк " << Max(s, 5); // Генерируется экземпляр String Max(String*, int)
cout << "\n Мax среди целых чисел " << Max(a, 9); // Генерируется экземпляр int Max(int *, int)
Bul_bul(s, 5); // Генерируется экземпляр void Bul_bul(String *, int );
cout << "\nОтсортированные строки ";
for(i = 0; i < 5; i++)
out2(s[i], ' '); // генерируется экземпляр void out2(String , char );
Bul_bul(a, 9); // генерируется экземпляр void Bul_bul(int *, int);
cout << "\nОтсортированные целые числа\n";
for(i = 0; i < 9; i++)
out2(a[i], ' '); // генерируется экземпляр void out2(int , char );
cout << "\nКомплексные числа\n";
for(i = 0; i < 5; i++)
out2(b[i], " "); // генерируется экземпляр void out2(Complex, char *)
out2(endl, "*********"); // генерируется экземпляр void out2(char, char *)
getch();
}
При порождении экземпляров функции-шаблона сортировки Bul_bul(...) компилятор сгенерирует также 2 функции Swap –
Swap (String &, String &) и Swap(int &, int &).
Программа будет работать при выполнении следующих условий:
в классе String должна быть перегружена операции сравнения на > ;
в классах String и Complex должна быть перегружена операция потокового вывода <<;
в классе String, кроме того, должна быть перегружена операция =.
Заметим, что функции-шаблоны напоминают перегруженные, но они по своим возможностям более ограничены. В теле таких функций должен быть одинаковый код (тело функции), а перегруженные функции могут содержать различные действия.
