АвтоАвтоматизацияАрхитектураАстрономияАудитБиологияБухгалтерияВоенное делоГенетикаГеографияГеологияГосударствоДомДругоеЖурналистика и СМИИзобретательствоИностранные языкиИнформатикаИскусствоИсторияКомпьютерыКулинарияКультураЛексикологияЛитератураЛогикаМаркетингМатематикаМашиностроениеМедицинаМенеджментМеталлы и СваркаМеханикаМузыкаНаселениеОбразованиеОхрана безопасности жизниОхрана ТрудаПедагогикаПолитикаПравоПриборостроениеПрограммированиеПроизводствоПромышленностьПсихологияРадиоРегилияСвязьСоциологияСпортСтандартизацияСтроительствоТехнологииТорговляТуризмФизикаФизиологияФилософияФинансыХимияХозяйствоЦеннообразованиеЧерчениеЭкологияЭконометрикаЭкономикаЭлектроникаЮриспунденкция

Шаблоны функций. Шаблон простой функции. Шаблон функции с несколькими аргументами

Читайте также:
  1. II. Основные задачи и функции
  2. III. Предмет, метод и функции философии.
  3. XVIII. ПРОЦЕДУРЫ И ФУНКЦИИ
  4. А) ПЕРЕДАЧА НА РУССКОМ ЯЗЫКЕ ФУНКЦИИ АРТИКЛЯ
  5. А. Средняя квадратическая погрешность функции измеренных величин.
  6. Абстрактные классы и чистые виртуальные функции. Виртуальные деструкторы. Дружественные функции. Дружественные классы.
  7. Адаптивные функции
  8. Администраторы судов, их функции
  9. Алгебраическое интерполирование функции.
  10. Анализ функции логики высказываний
  11. Аналитические функции
  12. Арендная плата: состав и функции

Шаблоны функций

Допустим, вам нужно написать функцию вычисления модуля чисел. Из школь-

ного курса алгебры вы, конечно, знаете, что модуль — это абсолютное значение

числа, то есть число без знака. Напоминаем: модуль числа 3 равен 3, модуль чис-

ла -3 равен 3.

Скорее всего, функция вычисления модуля будет использоваться с каким-

либо одним типом данных:

int abs(int n) //вычисление модулей целых чисел

{

return (n<0) ? -n : n; //если отрицательное, вернуть -n

}

Определенная выше функция берет аргумент типа int и возвращает резуль-

тат того же типа. Но теперь представьте, что понадобилось найти модуль числа

типа long. Придется писать еще одну функцию:

_640________________________________________________________________

long abs(long n) //вычисление модулей чисел типа long

{

return (n<0) ? -n : n;

}

Да, теперь еще одну для float:

float abs(float n) //модуль целых чисел

{

return (n<0) ? -n : n; //если отрицательное, вернуть -n

}

Тело функций, как видите, ничем не отличается. И все же они совершенно

разные, поскольку обрабатывают аргументы и возвращают значения разных типов.

Они могут быть перегружены и иметь одинаковые имена, это правда, но все равно

для каждой из них нужно писать отдельное определение. (Что касается языка С,

в котором нет понятия перегрузки, нельзя использовать даже одинаковые имена

для обозначения функций разных типов. Там это приведет к созданию целого кла-

на функций с похожими названиями, таких, как abs(), fabs(), fabs1(), labs() и cabs().)

Многократное переписывание этих функций-близнецов очень утомляет, за-

трудняет читабельность листинга, зато делает его увесистым. Можно показать

неопытному заказчику тома исходных кодов, демонстрируя свою напряженную

работу над проектом. А если где-то в алгоритме попадется ошибка, придется ис-

правлять ее в теле каждой функции. Причем некорректное исправление ошибки

приведет к несостоятельности программы.

Было бы здорово, если бы был какой-то способ написать эту функцию всего

один раз и заставить ее работать с различными типами данных, возвращая, со-

ответственно, результаты разного типа.

Шаблон простой функции

Первый пример в этой главе показывает, как пишется шаблон функции, вычис-



ляющей модуль числа. Шаблон работает со всеми базовыми числовыми типами.

В программе сначала определяется шаблонная версия abs(), а затем в main() про-

изводится проверка правильности ее работы.

Листинг 14.1. Программа TEMPABS

// tempabs.cpp

// Шаблон функции вычисления модуля числа

#include <iostream>

using namespace std;

//---------------------------------------------------------

template <class T> //Шаблон функции!

T abs(T n)

{

return (n < 0) ? -n : n;

}

//---------------------------------------------------------

int main()

{

int int1 = 5;

int int2 = -6;

long lon1 = 70000L;

long lon2 = -80000L;

double dub1 = 9.95;

double dub2 = -10.15;

//осуществления вызовов

cout << "\nabs(" << int1 << ")=" << abs(int1); //abs(int)

cout << "\nabs(" << int2 << ")=" << abs(int2); //abs(int)

cout << "\nabs(" << lon1 << ")=" << abs(lon1); //abs(long)

cout << "\nabs(" << lon2 << ")=" << abs(lon2); //abs(long)

cout << "\nabs(" << dub1 << ")=" << abs(dub1); //abs(double)

cout << "\nabs(" << dub2 << ")=" << abs(dub2); //abs(double)

cout << endl;

return 0;

}

 

Результаты работы программы:

abs(5)=5

abs(-6)=6

abs(70000)=70000

abs(-80000)=80000

abs(9.95)=9.95

abs(-10.15)=10.15

Итак, теперь функция abs() может работать со всеми тремя типами данных

(int, long, double). Типы определяются функцией при передаче аргумента. Она

будет работать и с другими базовыми числовыми типами, да даже и с пользо-

вательскими, лишь бы был надлежащим образом перегружен оператор «мень-

ше» (<) и унарный оператор «-».

‡агрузка...

Вот спецификация функции abs() для работы с несколькими типами данных:

template <class Т> //шаблон функции

T abs (Т n)

{

return (n<0) ? -n : n;

}

Вся конструкция, начиная от ключевого слова template в начале первой строки и включая определение функции, называется шаблоном функции. Каким образом достигается такая потрясающая гибкость использования функции?

Синтаксис шаблона функции

Краеугольным камнем концепции шаблонов функции является представление использующегося функцией типа не в виде какого-либо специфического (напри-мер, int), а с помощью названия, вместо которого может быть подставлен любой тип. В приведенном примере таким именем является Т (никакого сакрального смысла именно в таком названии нет. На его месте может все что угодно, например anyType или YooHoo). Ключевое слово template сообщает компилятору о том, что

«сейчас будет море крови»: мы определяем шаблон функции. Ключевое слово class, заключенное в угловые скобки, можно с тем же успехом заменить на type. Как вы уже видели, можно определять собственные типы, используя классы, поэто-

му разницы между типами и классами в известном смысле нет совсем. Переменная, следующая за словом class (Т в нашем примере), называется аргументом шаблона.

Внутри всего определения шаблона любой конкретный тип данных, такой, как

int, заменяет аргумент Т. В abs() это имя встречается лишь дважды в первой строке

в качестве типа аргумента и одновременно в качестве типа функции. В более

сложном случае оно могло бы встретиться много раз, в том числе в теле функции.

Действия компилятора

Что компилятор делает, увидев ключевое слово template и следующее за ним определения? Правильно, почти ничего или очень мало. Дело в том, что сам по себе шаблон не вызывает генерацию компилятором какого-либо кода. Да и не может он этого сделать, поскольку еще не знает, с каким типом дан-

ных будет работать данная функция. Он разве что запоминает шаблон для буду-щего использования.

Генерации кода не происходит до тех пор, пока функция не будет реально вызвана в ходе выполнения программы. В примере TEMPABS это происходит в

конструкциях типа abs(int) в выражении

cout << "\nabs(" << int1 << ")=" << abs(int1);

Когда компилятор видит такой вызов функции, он знает, что нужно исполь-

зовать тип int, поскольку это тип аргумента int1, переданного в шаблон. Поэто-

му он генерирует код abs для типа int, подставляя его везде вместо Т. Это называется реализацией шаблона функции; при этом каждый реализованный шаблон

функции называется шаблонной функцией1 Компилятор также генерирует вызов реализованной функции и вставляет

его туда, где находится abs(int1). Выражение abs(lon1) приводит к тому, что компилятором генерируется версия abs(int), работающая с типом long, и ее вызов.

То же самое касается типа float. Конечно, компилятор достаточно умен для того,

чтобы создавать для каждого типа данных только одну копию abs(). Таким образом, несмотря на два вызова int-версии функции, в исполняемом коде она будет рисутствовать только один раз.

Шаблоны функций с несколькими аргументами Взглянем на другой пример шаблона функции. У него три аргумента, два из

которых шаблонные, а один — базового типа. Функция предназначена для поиса в массиве заданного числа. Она возвращает индекс найденного значения

или -1 в случае его отсутствия в массиве. Аргументами являются указатель на массив, значение, которое нужно искать, а также размер массива. В main() мы определяем четыре различных массива различных типов и четыре значения, которые нужно найти. Тип char в данном примере воспринимается как число.

Для каждого массива вызывается шаблонная функция.

Листинг 14.2. Программа TEMPFIND

// tempfind.cpp

// Шаблон функции поиска в массиве

#include <iostream>

using namespace std;

// функция возвращает индекс или –1 при отсутствии //совпадения

template <class atype>

int find(atype* array, atype value, int size)

{

for(int j=0; j<size; j++)

if(array[j]==value)

return j;

return -1;

}

char chrArr[] = {1, 3, 5, 9, 11, 13}; //массив

char ch = 5; //искомое значение

int intArr[] = {1, 3, 5, 9, 11, 13};

int in = 6;

long lonArr[] = {1L, 3L, 5L, 9L, 11L, 13L};

long lo = 11L;

double dubArr[] = {1.0, 3.0, 5.0, 9.0, 11.0, 13.0};

double db = 4.0;

 

int main()

{

cout << "\n 5 в chrArray: индекс=" << find(chrArr, ch, 6);

cout << "\n 6 в intArray: индекс=" << find(intArr, in, 6);

cout << "\n11 в lonArray: индекс=" << find(lonArr, lo, 6);

cout << "\n 4 в dubArray: индекс=" << find(dubArr, db, 6);

cout << endl;

return 0;

}

 

Здесь, как видите, мы называем шаблонный аргумент именем atype. Оно по-

является два раза в аргументах: как тип указателя на массив и как тип искомого

значения. Третий аргумент — размер массива — всегда типа int. Это не шаблон-

ный аргумент. Вот результаты работы программы:

5 в chrArray: индекс=2

6 в intArray: индекс=-1

11 в lonArray: индекс=4

4 в dubArray: индекс=-1

Компилятор генерирует четыре разных варианта функции, по одному на каждый тип. Функция находит число 5 в ячейке массива с индексом 2, не находит число 6 в массиве вообще и т. д.

Аргументы шаблона должны быть согласованы При вызове шаблонной функции все экземпляры данного аргумента шаблона

должны быть строго одного типа. Например, в find() есть массив типа int, соответственно, искомое значение должно быть того же типа. Нельзя делать так:

int intarray[]={1,3,5,7}; //массив int

float fl = 5.0; //значение float

int value = find(intarray, f1, 4);//ax. ox!

Компилятору нужно, чтобы все экземпляры atype были одного типа. Он может генерировать код функции Find(int*, int, int);

но не станет заниматься такими глупостями:

find(int*, float, int);

потому что первый и второй аргумент должны быть одного типа.

А почему не макросы?

Старосветские программисты, пишущие на C, снова удивятся, почему же мы не спользуем макросы для создания функций, работающих с разными типами дан-

ных? Например, функция abs() могла бы быть определена так:

#define abs(n) ( (n<0) ? (-n) : (n) )

Это возымело бы тот же эффект, что и от шаблона класса в TEMPABS, потому что здесь используеся простое замещение одного текста на другой, а значит, можно указывать любые типы данных. Тем не менее, как уже отмечалось ранее,

макросы редко используются в C++. С ними возникают некоторые проблемы.

Одна из них заключается в том, что макросы не осуществляют проверку типов. Может быть несколько переменных макроса, которые обязаны быть одного типа, но компилятор не проверяет это условие. Еще одна проблема состоит в том, что не указан тип возвращаемого значения, поэтому компилятор, опять же, не может

проверить корректность присваивания. В любом случае, макросы ограничены функциями, которые можно выразить с помощью всего одного выражения. Да есть еще и другие проблемы с макросами, которые привели к выходу их из моды

при программировании на C++. В целом, лучше избегать их использования.

 


1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 |


При использовании материала, поставите ссылку на Студалл.Орг (0.023 сек.)