
- •Алгоритм
- •3. Этапы решения задач на эвм.
- •4. Структу́рное программи́рование
- •6. Типизированные указатели.
- •7.Ссылочный тип.
- •8.Перечислимый тип.
- •10.Процедуры ввода-вывода. Потоковый ввод-вывод.
- •13.Условный оператор. Оператор выбора.
- •14.Операторы безусловного перехода.
- •16.Циклические программы. Вложенные циклы.
- •17.Глобальные и локальные переменные.
- •18.Функции. Механизм передачи параметров.
- •19.Вложенные функции. Рекурсия.
- •20.Область видимости и время жизни переменной.
- •21.Фактические и формальные параметры.
- •22.Перегрузка функций.
- •23.Шаблоны функций.
- •24.Понятие модуля. Преимущества модульного программирования. Структура модуля.
- •25.Пример модуля. Способ использования модуля.
- •27.Символьный тип данных. Строковые массивы. Способы обработки.
- •28.Основные операции над строками. Функции для работы со строками.
- •29.Структуры. Работа со структурами. Примеры.
- •30.Массивы структур. Особенности обработки. Примеры.
- •31.Файлы. Виды файлов. Файловая переменная. Общая схема работы с файлами.
- •33.Текстовые файлы. Функции обработки для текстовых файлов.
- •34.Бинарные файлы. Функции для работы с бинарными файлами.
- •35.Статическая и динамическая память.
- •37.Алгоритмы и методы сортировки: оценка эффективности алгоритма.
- •38.Сортировка выбором.
- •43.Алгоритмы и методы поиска в отсортированном массиве данных.
23.Шаблоны функций.
Шаблоны функций, своими словами,— это инструкции, согласно которым создаются локальные версии шаблонированной функции для определенного набора параметров и типов данных.
На самом деле, шаблоны функций -это мощный инструмент в С++, который намного упрощает труд программиста. Например, нам нужно запрограммировать функцию, которая выводила бы на экран элементы массива. Задача не сложная! Но, чтобы написать такую функцию, мы должны знать тип данных массива, который будем выводить на экран. И тут нам говорят — тип данных не один, мы хотим, чтобы функция выводила массивы типа int, double, float и char.
Как оказалось, задача усложнилась. И теперь мы понимаем, что нам нужно запрограммировать целых 4 функции, которые выполняют одни и те же действия, но для различных типов данных. Так как мы еще не знакомы с шаблонами функций, мы поступим так: воспользуемся перегрузкой функций.
// перегрузка функции printArray для вывода массива на экран
void printArray(const int * array, int count)
{
for (int ix = 0; ix < count; ix++)
cout << array[ix] << " ";
cout << endl;
}
void printArray(const double * array, int count)
{
for (int ix = 0; ix < count; ix++)
cout << array[ix] << " ";
cout << endl;
}
void printArray(const float * array, int count)
{
for (int ix = 0; ix < count; ix++)
cout << array[ix] << " ";
cout << endl;
}
void printArray(const char * array, int count)
{
for (int ix = 0; ix < count; ix++)
cout << array[ix] << " ";
cout << endl;
}
Таким образом, мы имеем 4 перегруженные функции, для разных типов данных. Как видите, они отличаются только заголовком функции, тело у них абсолютно одинаковое. Я написал один раз тело функции для типа int и три раза его скопировал для других типов данных.
И, если запустить программу с этими функциями, то она будет исправно работать. Компилятор сам будет определять какую функцию использовать при вызове.
Как видите, кода получилось достаточно много, как для такой простой операции. А что если, нам понадобится запрограммировать алгоритм сортировки в виде функции. Получается, что для каждого типа данных придется свою функцию создавать. То есть, сами понимаете, что один и тот же код будет в нескольких экземплярах, нам это ни к чему. Поэтому в С++ придуман такой механизм — шаблоны функций.
Мы создаем один шаблон, в котором описываем все типы данных. Таким образом исходник не будет захламляться никому ненужными строками кода. Ниже рассмотрим пример программы с шаблоном функции. Итак, вспомним условие: «запрограммировать функцию, которая выводила бы на экран элементы массива».
#include <iostream>
#include <cstring>
using namespace std;
// шаблон функции printArray
template <typename T>
void printArray(const T * array, int count)
{
for (int ix = 0; ix < count; ix++)
cout << array[ix] << " ";
cout << endl;
} // конец шаблона функции printArray
int main()
{
// размеры массивов
const int iSize = 10,
dSize = 7,
fSize = 10,
cSize = 5;
// массивы разных типов данных
int iArray[iSize] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
double dArray[dSize] = {1.2345, 2.234, 3.57, 4.67876, 5.346, 6.1545, 7.7682};
float fArray[fSize] = {1.34, 2.37, 3.23, 4.8, 5.879, 6.345, 73.434, 8.82, 9.33, 10.4};
char cArray[cSize] = {"MARS"};
cout << "\t\t Шаблон функции вывода массива на экран\n\n";
// вызов локальной версии функции printArray для типа int через шаблон
cout << "\nМассив типа int:\n"; printArray(iArray, iSize);
// вызов локальной версии функции printArray для типа double через шаблон
cout << "\nМассив типа double:\n"; printArray(dArray, dSize);
// вызов локальной версии функции printArray для типа float через шаблон
cout << "\nМассив типа float:\n"; printArray(fArray, fSize);
// вызов локальной версии функции printArray для типа char через шаблон
cout << "\nМассив типа char:\n";printArray(cArray, cSize);
return 0;
}
Заметьте, код уменьшился в 4 раза, так как в программе объявлен всего один экземпляр функции — шаблон. В main я объявил несколько массивов — четыре, для типов данных: int, double, float, char. После чего, в строках 26, 28, 30, 32, выполняется вызов функции printArray для разных массивов. Обратите внимание, что перед объявлением самой функции, в строке 5, стоит следующая запись template<typenameT>. Как раз эта запись и говорит о том, что функция printArray на самом деле является шаблоном функции, так как в первом параметре printArray стоит тип данных const T*, точно такой же как и в строке 5.
Все шаблоны функций начинаются со слова template, после которого идут угловые скобки, в которых перечисляется список параметров. Каждому параметру должно предшествовать зарезервированное слово class или typename.1 template <class T>
или1 template <typename T>
или1 template <typename T1, typename T2>
Ключевое слово typename говорит о том, что в шаблоне будет использоваться встроенный тип данных, такой как: int, double, float, char и т. д. А ключевое слово class сообщает компилятору, что в шаблоне функции в качестве параметра будут использоваться пользовательские типы данных, то есть классы.
У нас в шаблоне функции использовались встроенные типы данных, поэтому в строке 5 мы написали template<typenameT>. Вместо T можно подставить любое другое имя, какое только придумаете. Давайте подробно рассмотри фрагмент кода из верхней программы, я его вынесу отдельно.1
2
3
4
5
6
7
8 // шаблон функции printArray
template <typename T>
void printArray(const T * array, int count)
{
for (int ix = 0; ix < count; ix++)
cout << array[ix] << " ";
cout << endl;
} // конец шаблона функции printArray
В строке 2 выполняется определение шаблона с одним параметром — T, причем этот параметр будет иметь один из встроенных типов данных, так как указано ключевое слово typename.
Ниже, в строках 3 — 8 объявлена функция, которая соответствует всем критериям объявления обычной функции, есть заголовок, есть тело функции, в заголовке есть имя и параметры функции, все как обычно. Но что эту функции превращает в шаблон функции, так это параметр с типом данных T, это единственная связь с шаблоном, объявленным ранее. Если бы мы написали1
2
3
4
5
6 void printArray(const int * array, int count)
{
for (int ix = 0; ix < count; ix++)
cout << array[ix] << " ";
cout << endl;
}
то это была бы простая функция для массива типа int.
Так вот, по сути T — это даже не тип данных, это зарезервированное место под любой встроенный тип данных. То есть когда выполняется вызов этой функции, компилятор анализирует параметр шаблонированной функции и создает экземпляр для соответственного типа данных: int, char и так далее.
Поэтому следует понимать, что даже если объем кода меньше, то это не значит, что памяти программа будет потреблять меньше. Компилятор сам создает локальные копии функции-шаблона и соответственно памяти потребляется столько, как если бы вы сами написали все экземпляры функции, как в случае с перегрузкой.
Надеюсь основную мысль по шаблонам функций до вас довел. Для закрепления материала, давайте рассмотрим еще один пример программы, с использованием шаблона функции.1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24 #include <iostream>
#include <cstring>
using namespace std;
// шаблон функции для поиска максимального значения в массиве
template <typename T>
T searchMax(const T* array, int size)
{
T max = array[0]; // максимальное значение в массиве
for (int ix = 0; ix < size; ix++)
if (max < array[ix])
max = array[ix];
return max;
}
int main()
{
// тестируем шаблон функции searchMax для массива типа char
char array [] = "aodsiafgerkeio";
int len = strlen(array);
cout << "Максимальный элемент массива типа char: " << searchMax(array, len) << endl;
// тестируем шаблон функции searchMax для массива типа int
int iArray [5] = {3,5,7,2,9};
cout << "Максимальный элемент массива типа int: " << searchMax(iArray, 5) << endl;
return 0;
}
Вот вам еще один пример использования шаблонов функций. Шаблон функции объявлен в строках 5-13. Функция должна возвращать максимальное значение массива, поэтому возвращаемое значение типа T, ведь тип данных массива заранее не известен. Кстати внутри функции объявлена переменная max типа T, в ней будет храниться максимальное значение массива. Как видите, тип данных T используется не только для спецификации параметров функции, но и для указания типа возвращаемого значения, а также может свободно использоваться для объявления любых переменных внутри шаблона функции.
Шаблоны функций также можно перегружать другими шаблонами функций, изменив количество передаваемых параметров в функцию. Еще одной особенностью перегрузки является то, что шаблонные функции могут быть перегружены обычными не шаблонными функциями. То есть указывается тоже самое имя функции, с теми же параметрами, но для определенного типа данных, и все будет корректно работать.