Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:

chast1

.pdf
Скачиваний:
17
Добавлен:
10.06.2015
Размер:
499.82 Кб
Скачать

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

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

________________________________________________________________________________

38.Шаблоны функций - основные понятия (шаблон, как определитель семейства функций; виды параметров шаблонов;стандартизованный формат обращения к шаблонным функциям.

________________________________________________________________________________

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

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

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

Формат шаблона семейства функции имеет вид: template<список параметров шаблона> параметризованное определение шаблонной функции

Здесь служебное слово template и <> в которых список параметров шаблона, являются обязательными элементами синтаксиса. Часть формата от template до > называют заголовком шаблона.

Параметрышаблона могутбыть кактипизирующими так и нетипизирующими. Именно типизирующие параметры предназначены для управления типамиданных в процессе обращения к функции. Объявление каждого типипзирующего параметра должно начинаться со служебного слова class или typename после которого указывается имя параметра.

Рассмотрим пример, иллюстрирующий и это правило и в целом формат задания шаблона семейства функций. Определим шаблон функций для получения абсолютнонго значениячислового аргумента (модуль)

template <class TIPE> //(1) TIPE abs(TIPE x) //(2) {returne x>0 ? x :-x;} //(3)

2-3 параметризованное определение шаблона функции.

В этом определении типизирующий параметр имеет имя TIPE. Это используется для параметризованного определения типа возвращаемого результата функции и параметра функции.

Определим шаблон функций для обмена значений двух аргументов. template <typename T> //(4)

void swap (T*x,T*y) //(5) аргументы здесь должныпередаваться по адресу, поскольку результам работы функции являются измененные значения параметров.

{T z=*x; //(6) *x=*y;//(7)

*y=z:}//(8) в этом определение типизирующий параметр имеет имя Т. Это имя используется для параметризованного описания типов двух параметров функции и локально определяемой в функции переменной z.

Полностью стандартизованный формат обращения к шаблонам функциям имеет вид:

имя шаблонной функции <список аргументов шаблона> (список аргументов шаблонной функции) все скобки обязательны.

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

double x1=1.1,y1=-33.33, z1;//(9) определены вспомогательные вещественные переменные z1=abs<double> (y1);//(10) полностью стандартизованный вызов функции abs swap<double>(&x1,&y1);//(11)полностью стандартизованный вызов функции swap

в строках 10-11 приведен пример замены типизирующего параметра TIPE и Т типом double.

Приведем аналогичные примеры, в которых типизирующие параметры заменяются типом int: int i1=1, j1=-33, k1;//(12)

k1=abs<int>(j1);//(13)

swap<int>(&i1,&j1);//(14)

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

Если компелятор встречает в тексте вызов шаблонной функции,в котором типизирующий параметр конкретизирован (в нашем примере типом double), то он формирует так называемую неявную специализацию шаблонной функции, т.е. фактически создает код конкретной функции, в котором вместо имени параметра подставляется конкретный тип (double). Процесс создания специализации принято называть инстанциированием шаблона. А далее в процесе выполнения программы указанный код конкретной функции срабатывает кака вызов обычной функции.

__________________________________________________________________________________

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

__________________________________________________________________________________

Стандарт языка с++ допускает отступления от полностью стандартизированного вызова шаблонной функции, а именно если типы аргументов шаблона могут быть однозначно определены на основе анализа списка фактических параметров функции в ее вызове, то список аргументов шаблонов можно опускать. В нашем примере вместо строки 10 можно было написать:

z1=abs(y1);//(10') здесь неявное задание аргументашаблона. Здесь необходимая информация о конктетном типе, который должен быть использован привызове функции содержится в фактическом параметре y1, поскольку у1 относится к типу double, то это является сигналом для компилятора о том,что параметр TIPE надо заменить на double.

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

Пусть мы хоттим определить функцию, которая осуществляет деление двух чисел. Причом мы хотим управлять характером деления, т.е. мы хотим, чтобы работали наши, а не стандартные правила выполнения операции /. Мы независимо от типа фактических параметров должны осуществить либо вещественное деление (double) либо деление с отбрасыванием дробной части (int)/ Такую задачу может выполнить следующий шаблон функции:

template <class A> //(15) A divide (Ax,Ay) //(16) {returne x/y;} //(17)

Тогда при вызове

divide <double>(t1,t2) //(18)

Независимо от параметров т1 и т2 функция вернетрезультат вещественного деления. divide <double>(7,2); //(19)

Функция вернет значение 3.5 Если же мы хотим , чтобы функция опять независимо от типов фактических параметров возвращала целую

часть от деления,то обращение должно иметь вид: divide <int>(t1,t2); //(20)

Тогда при обращении к divide <int>(7.4,2.1);//(21)

Функция вернет значение 3 Однако, если отказаться от полностью стандартизированного формата обращения к функции и вместо 21 написать

divide(7.4,2.1) //(21')

то возвращаемое значение будет равно 3.52381, поскольку по правилам обработки вызоавов шаблонных функций типизирующий параметр Абудет интерпритирован,как double.

Рекомендация.

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

Замечание.

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

__________________________________________________________________________________

Нетипизирующие параметрышаблона функций и непараметризованные параметры функции.

_________________________________________________________________________________

Помимо типизирующих параметров в состав списка параметров шаблона могут входить и нетипизирующие параметры,а в состав списка параметров шаблонной функции могут входить непараметризованные параметры.

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

элементов одномерного массива. При этом тип элементов массива может меняться.

Т -типизирующий параметр шаблона,задающий тип элементов массива и тип возвращаемого результата. N -индекс первого элемента,суммируеиого фрагмента массива.

len -длина суммируемого фрагмента массива.

Шаблон функции будет иметь вид: template <int N, typename T> //(22) T sum(T ar[], int len=1)//(23)

{T s=T(0);//(24) здесь выражение Т(0) это функциональная форма операция преобразования аргумента (в данном примере 0) к типу Т.

for(int i=N; i<N+len; i++) s+=ar[i];//(25) return s;}//(26)

Здесь в списке <int N,typename T>первый параметр нетипизирующий. При обращении к функции его нужно заменить значением указанного типа (варианты запрещены). В списке параметров шаблонной функции(T ar[], int len=1) второй параметр непараметризованный в том смысле, что его тип задается не через абстрактное имя Т,а непосредственно. Такие параметры можно задавать со значением по умолчанию, хотя как и в случае перегруженных функций умалчиваемыми параметрами надо пользоваться осторожно.

Пример обращения к функции sum

cout<<"сумма фрагмента массива ="<<sum<2,double>(mas,2)<<endl;//(27)

общее замечание

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

2.нетипизирующие параметры явно специфицируются в заголовке шаблона. При этом на типы таких параметров наложены следующие ограничения:

- они не могут быть вещественными,не могут бытьклассами, не могут быть типа void.

- они могут быть целочисленными, указателями, ссылками на объект или функцию,указателями на поля данных и на методы класса.

3.В списке параметров шаблона функции может быть несколько типизирующищ параметров. Объявление каждого из них отдельно должно начинаться с со служебного слова class или typename.

4.аргумент,соответствующий типизирующему параметру шаблона может быть именем любого типа, как базового так и производного, но при этом тип должен соответствовать смыслу алгоритма, запрограммированного в теле функции.

5.Имя параметра шаблона должно быть уникальным во всем определении данного шаблона.

6.имя типизирующего параметра шаблона может использоваться для спецификации параметров шаблонной функции,возвращаемого ее результата и любых локальных объектов в теле функций.Имя параметра должно быть видно во всем определении шаблона и при этом оно скрывает другие использования этого же индитификатора в этой области видимости. Если в теле функции необходим доступ к внешним объектам с теми же именами, то нужно применять операцию кузания области видимости (::)

________________________________________________________________________________

39.Подставляемые функции.

________________________________________________________________________________

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

Следует учитывать, что при таком режиме работы функции, время тратития на собственно выполнение кода функции, но и на сам акт вызова функции в частности на поиск адреса функции в основной памяти компьютера. Если вызовов данной функции в программе много, то такие дополнительные затраты времени могут оказаться значительными. Избежатьих можно если воспользоваться подставляемыми (встраиваемыми) функциями.

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

Для примера рассмотрим функцию, расчитывающую значение полинома второй степени: inline double f2pol (double a,double b,double c, double x)//(1)

{returne a*x*x+b*x+c;}//(2)

Главная отличительная особенность в том , что компелятор в процессе анализа программы вставляет в точке вызова функциине команды передачи управления единственному экземпляру кода функции,а сам код тела функции,следовательно при наличие в программе ,например,трех вызовов функции f2pol после компеляции прграммы возникает объектный код, в котором в соответствующих тех местах будет повторен код тела функции с заменой формальных параметров фактическими. В результате при выполнении программы время на передачу управления тратиться не будет. Помимо экономии времени прямая постановка ода функции в текст программы в даной ее точке, позволяет компелятору увидеть контекст выполнения кода функции. И длагодаря этому появляется дополнителтная возщможность провести оптимизацию вычислительного алгоритма.

Правило работы спецификатора inline

1 Спецификатор inline задает так называемое внутреннее связыввание определяемой функции, т.е. она будет видна только в том файле,в котором определена. Попытка объявить в другом файле ее прототип будет индентефицирована как ошибка.

2. не всякая фукция будет иниерпритирована, как подставляемая. Окончательный результат зависит от реализации компелятора. Чаще всего проблемы возникают, если функция рекурсивна и вызыватся с помошью указателя на нее. Если компилятор даже при наличие спецификатора inline не считает возможным применить функцию, как подставляемую, то ее вызов осуществляется обычным способом.

рекомендация.

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

________________________________________________________________________________

40.Целесообразное размещение определений функций в программе.

_______________________________________________________________________________

Если разрабатывается большой многофайловый проект и в одном основном файле (или в нескольких основных) используются общие функции, типа данных и переменные, то целесообразно применять следующий прием объявления в основном (основных) файле внешних объектов, т.е. определенных в других файлах.

С помощью операции среды Visual Studio Проект/Добавитьновый элемент/Заголовочный файл (.h) в проекте будет добавлен заголовочный файл. Пустьего имя будет "Личн_загол_1.h", в этом файле заключают определения всех общих для проекта типов. Кроме того в этот же заголовочный файл можно добавить директивы включения стандартных заголовочных файлов, которые необходыми в основных файлах. Аналогично можно добавить еще один заголовочный файл: "Личн_загол_2.h". В него можно включить объявления прототипов всех необходимых в основных файлах функций. Сюда же с помощью спецификатора Extern добавить объявления переменных определенных в других файлах. После этого указанные заголовочные файлы директивами

#include"Личн_загол_1.h" #include"Личн_загол_2.h"

Включают в основные файлы проекта. Указанные директивы должны располагаться после директивы

#include"stdafx.h".

Для иллюстрации указанного приведем "Эскиз проекта", состоящего из 4 файлов (модулей). Для конкретности будем полагать, что проект предназначен для работы с хранилищем данных, построенном на двоичном дереве.

Смысл 4 файлов проектов таков:

-основной файл "Рекурс_фун+Личн_загол_файлы.срр"

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

-заголовочный файл "Личн_загол_1.h"

-заголовочный файл "Личн_загол_2.h"

Замечание.

Чтобы понять всю целесообразность такой структуры программы,необходимо вообразить,что в файле "Личн_загол_2.h" собраны прототипы функций из нескольких файлов, а не из одгого файла "Определение_функций.срр",а также, что заголовочные файлы нужны не в одном файле, как на рисунке,а в нескольких основных.

Соседние файлы в предмете [НЕСОРТИРОВАННОЕ]