Объектно-ориентированное программирование — Шаблоны функций
- Объектно-ориентированное программирование
- Локальные и глобальные переменные
- Подпрограммы и их аргументы
- Определение данных
- Операторы динамического распределения памяти
- Перегрузка функций и операций
- Правила составления перегружаемых функций и операций
- Класс как обобщение структуры
- Определение первичного класса
- Перегрузка операций
- Конструкторы
- Список инициализации
- Деструктор
- Дружественные классы
- Статические элементы класса
- Шаблоны функций
Многие алгоритмы не зависят от типа данных, которые они обрабатывают. Например, перестановка двух переменных:
Type x, y, temp;
. . .
temp = x; x = y; y = temp;
будет работать для Type = int, для Type = double и для любого нового типа, определенного в программе с помощью класса. Логика алгоритма одинакова для всех типов данных. Эту ситуацию можно обобщить. Многие алгоритмы допускают отделение метода от данных. Программы, реализующие такие алгоритмы, можно отлаживать для данных одного типа, а затем применять для обработки данных других типов. Функции, реализующие эту возможность, называются параметризованными. Аргументы этих функций определяются с помощью ключевого слова template. Тип, который определяет это ключевое слово, называется шаблоном. Общая форма определения шаблона функции template приведена ниже:
template прототип функции (аргументы)
{
тело функции;
}
Здесь символ Т обозначает тип данных, используемый функцией, прототип функции состоит из типа возвращаемого значения и имени функции. Например, алгоритм перестановки значений двух аргументов можно реализовать следующим образом:
template void swap(Tswp& x, Tswp& y)
{
Tswp temp;
temp = x; x = y; y = temp;
}
void main()
{
int a = 1, b = 2;
double c = 1.1, d = 2.2;
swap(a, b); // Перестановка целых чисел
swap(c, d); // Перестановка чисел с плавающей точкой
}
Таким образом, для объявления шаблона функции, функция описывается стандартным образом, но перед ее прототипом ставится ключевое слово template, за которым следует заключенный в угловые скобки список параметров. Каждый из этих параметров определяется с помощью ключевого слова class, за которым следует идентификатор. Идентификатор служит для замещения имени типа. При вызове функции предполагается, что ключевое слово class в контексте шаблонов означает любой тип, а не только класс.
Например, максимум двух значений типа Т можно вычислять с помощью функции:
template // Ключевое слово и параметр
const T& Max(const T& a, const T& b)
{
return a>b? a:b;
}
void main()
{
int i = 1, j = 2;
float r = 1.1, s = 1.2;
int k = Max(i, j);
float t = Max(r, s);
}
Параметризованные функции могут быть перегружены другими функциями, которые тоже могут быть параметризованы, например:
template const T& Max(const T&, const T&);
template const T& Max(const T*, int);
int Max(int, int);
Для определенных типов эти функции могут быть перегружены (и переопределены) для того, чтобы выполнять (или не выполнять) какие-либо действия, которые функции-шаблоны не выполняют (или выполняют), например:
const char* Max(const char* c, const char* d)
{
// Выполнить действия, специфичные для char*
}
Пример. Параметризованная функция бинарного поиска в отсортированном массиве.
#include
#include
template
int binsearch(Type* x, int count, Type key)
{
int low, high, mid; // Левый, правый и средний элементы
low = 0; high = count — 1;
while (low <= high)
{
mid = (low+high)/2; // Вычисление середины массива
if(key < x[mid]) high = mid — 1; // Если нужный элемент
// находится слева от середины
else if(key > x[mid]) low = mid + 1; // Если справа
else return mid; // Нашли
}
return -1; // Не нашли
}
void main()
{
clrscr(); // Очистка экрана
int nums[] = {1, 2, 3, 5, 7, 11, 13, 17}; // Массив, в котором ищем
cout << "5 находится на " << binsearch(nums, 8, 5)
cout << " месте в массиве.";
getch(); // Ожидание нажатия клавиши
}
Результаты работы программы
5 находится на 3 месте в массиве.
Пример. Приведём параметризованную подпрограмму (функцию) сортировки методом пузырьков и применим ее для упорядочения массива, состоящего из целых чисел, записанных в неупакованном BCD — формате. Ниже следует подпрограмма (функция) сортировки и программа тестирования.
#include
#include
template
void bubble (Type *x, int n) // Сортировка методом пузырьков
{
int i, j;
Type t;
for(i = 1; i < n; i++)
for(j = n-1; j >= i; --j)
{
if(x[j-1] > x[j])
{
t = x[j-1]; x[j-1] = x[j]; x[j] = t;
}
}
}
void main()
{
clrscr(); // Очистка экрана
int i;
int nums[] = {10, 12, 11, 3, 5, 12, 10}; // Исходный массив
cout << "Исходный массив: ";
for(i = 0; i < 7; i++) cout << nums[i] << " ";
cout << '\n';
bubble (nums, 7); // Сортировка
cout << "Отсортированный массив: ";
for(i = 0; i < 7; i++) cout << nums[i] << " ";
getch(); // Ожидание нажатия клавиши
}
Результаты работы программы
Исходный массив: 10 12 11 3 5 12 10
Отсортированный массив: 3 5 10 10 11 12 12
В неупакованном BCD-формате старшим байтом представляется знак, младшая цифра числа записывается как нулевой элемент массива, следующая цифра — первый элемент массива и т.д.
Например, число 123 представляется байтами:
a[0] = ‘3’, a[1] = ‘2’, a[2] = ‘1’, a[n-1] = ‘-’.
Здесь n — количество разрядов числа.
Приведём пример программы для сортировки чисел, представленных в формате BCD. Для этого к параметризированной подпрограмме сортировки добавим определение класса чисел BCD и необходимые операции присваивания и сравнения. Получим следующий текст:
#include
#include
#include
template
void bubble (Type *x, int n) // Сортировка методом пузырьков
{
int i, j;
Type t;
for(i = 1; i < n; i++)
for(j = n-1; j >= i; --j)
{
if(x[j-1] > x[j])
{
t = x[j-1]; x[j-1] = x[j]; x[j] = t;
}
}
}
// Класс BCD чисел
class Bcd
{
// Недоступные элементы класса
static int n; // Максимальный размер BCD чисел
char *a; // Массив под BCD число
public:
// Общедоступные элементы класса
// Перегрузка оператора =
void operator = (char *b);
// Перегрузка оператора >
int operator > (Bcd x);
void show(); // Вывод BCD числа на экран
};
// Перегрузка оператора =
void Bcd::operator = (char *b)
{
int i;
a = new char[n]; // Выделение памяти под BCD число
for(i = 0; i < n; i++)
a[i] = '0'; // Инициализация его нулями
i = strlen(b); // Определение длины присваиваемого числа
int k = i — 1; // Запоминаем её
// Копирование знака числа
if(b[0] == '+' || b[0] == '-')
{
i--;
a[n — 1] = b[0];
}
else a[n — 1] = '+';
// Копирование самого числа
for(int j = 0; j < i; j++) a[j] = b[k — j];
}
// Перегрузка оператора >
int Bcd::operator > (Bcd x)
{
int i = 0;
// Если первое число положительное,
// а второе — отрицательное, то первое больше
if(this->a[n-1] == '+' && x.a[n-1] == '-') return 1;
// Если первое число отрицательное,
// а второе — положительное, то первое меньше
if(this->a[n-1] == '-' && x.a[n-1] == '+') return 0;
// Сравнение по отдельным цифрам
for(i = 1; i < n; i++)
{
if(this->a[n — 1 — i] > x.a[n — 1 — i])
{
if(x.a[n — 1] == '+') return 1;
else return 0;
}
else if(this->a[n — 1 — i] < x.a[n — 1 — i])
{
if(x.a[n — 1] == '+') return 0;
else return 1;
}
}
return 0;
}
// Вывод BCD числа на экран
void Bcd::show()
{
// Создание вспомогательной строки
char *str;
str = new char[n+1]; // Выделение под неё памяти
str[0] = a[n-1]; // Копирование знака
str[n] = '\0'; // Постановка конечного нуля
// Копирование цифр
int i;
for(i=n-2; i>=0; i--) str[n-i-1] = a[i];
// Вывод строки на экран
cout << str;
delete str; // Освобождение памяти
}
Теперь вызываем параметризованную функцию для сортировки массива Bcd:
int Bcd::n = 15; // Максимальная длина BCD числа
void main()
{
clrscr(); // Очистка экрана
Bcd x[10]; // Создание массива bcd чисел
// Инициализация BCD чисел
x[0] = "1234"; x[1] = "924"; x[2] = "-92"; x[3] = "0";
x[4] = "-1"; x[5] = "10"; x[6] = "12"; x[7] = "1";
x[8] = "-2"; x[9] = "12345";
// Вывод неотсортированного массива
cout << "Неотсортированные BCD числа:\n";
for(int i = 0; i < 10; i++)
{
x[i].show();
cout << '\n';
}
bubble(x, 10); // Сортировка методом пузырьков
// Вывод отсортированного массива
cout << "Отсортированные BCD числа:\n";
for(i = 0; i < 10; i++)
{
x[i].show();
cout << '\n';
}
getch(); // Ожидание нажатия клавиши
}
Результаты работы программы
Неотсортированные BCD числа:
+00000000001234
+00000000000924
-00000000000092
+00000000000000
-00000000000001
+00000000000010
+00000000000012
+00000000000001
-00000000000002
+00000000012345
Отсортированные BCD числа:
-00000000000092
-00000000000002
-00000000000001
+00000000000000
+00000000000001
+00000000000010
+00000000000012
+00000000000924
+00000000001234
+00000000012345
Пример. Рассмотрим параметризованную подпрограмму (функцию) сортировки методом выбора. Сначала находим наименьший элемент массива и переставляем его с первым элементом. Затем из оставшихся элементов находим наименьший и переставляем его со вторым элементом и т.д. Для того чтобы не переставлять элемент сам с собой в том случае, когда он уже является наименьшим среди элементов подмассива, определим переменную exchange, которая будет равна 0, если перестановки не нужны. Получим следующую подпрограмму:
template
void select(Type *x, int count)
{
int i, j, k, exchange;
Type t;
for(i=0; i
{
exchange=0;
k=i; t=x[i];
for(j=i+1; j
{
if(x[j]
{
k=j; t=x[j]; exchange=1;
}
}
if(exchange)
{
x[k]=x[i]; x[i]=t;
}
}
}
Вызов подпрограммы осуществляется слудующим образом :
int nums[] = {1, 3, 8, -1, 12, -1, 15};
Select(nums, 7);
Пример. Рассмотрим параметризованную функцию быстрой сортировки Хоара. Алгоритм быстрой сортировки опирается на идею разбиения массива на две части с последующим рекурсивным применением подпрограммы сортировки к каждой из этих частей. Перед разбиением выбирается некоторый элемент массива, значение которого называется компарандом.
Все элементы, значения которых меньше компаранда, переносятся в левую часть массива, а элементы, имеющие большее значение — в правую часть. Затем этот же процесс повторяется для каждой из частей. И так до тех пор, пока массив не будет отсортирован. Ниже приведён текст программы.
#include //библиотека потокового ввода-вывода
#include //библиотека консольного ввода-вывода
//параметризованная функция быстрой сортировки Хоара
template
void qs(Type *a, int left, int right)
{
int i, j; //левая и правая границы массива
Type x, y;
i = left; j = right;
x = a[(left+right)/2]; //определим "центр" массива
do
{
//произведём поиск элементов для перестановки
while(a[i]
while(xleft) j--;
if(i<=j)
{
//выполняем перестановку
y=a[i]; a[i]=a[j]; a[j]=y;
i++; j--;
}
}
while(i<=j);
if(left
if(i
}
//основная программа
void main()
{
int i;
int nums[]={5, 10, 12, 3, 8, 9, 2, 1}; //массив чисел для сортировки
clrscr();
cout<<"Входные данные (неотсортированный массив):\n";
for(i=0; i<8; i++) cout << nums[i] << " ";
qs(nums, 0, 7); //вызов подпрограммы сортировки
cout<<"\nВыходные данные (отсортированный массив):\n";
for(i=0; i<8; i++) cout << nums[i] << " ";//вывод результатов на экран
}
Результаты работы программы
Входные данные (неотсортированный массив):
5 10 12 3 8 9 2 1
Выходные данные (отсортированный массив):
1 2 3 5 8 9 10 12
- << Назад
- Вперёд