Функции с переменным числом параметров



 

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

 

тип имя (явные параметры,. . . )

{

тело функции

}

 

После списка обязательных параметров ставится запятая, а затем многоточие, которое показывает, что дальнейший контроль соответствия количества и типов параметров при обработке вызова функции производить не нужно. При обращении к функции все параметры и обязательные, и необязательные будут размещаться в памяти друг за другом. Следовательно, определив адрес обязательного параметра как p=&k, где p - указатель, а k – обязательный параметр, можно получить адреса и всех остальных параметров: оператор k++; выполняет переход к следующему параметру списка. Еще одна сложность заключается в определении конца списка параметров, поэтому каждая функция с переменным числом параметров должна иметь механизм определения количества и типов параметров. Существует два подхода:

1) известно количество параметров, которое передается как обязательный параметр;

2) известен признак конца списка параметров.

 

//Найти среднее арифметическое последовательности

//чисел, если известно количество чисел

#include <iostream.h>

float sum(int k, . . .)

//явный параметр k задает количество чисел

{

int *p=&k;//настроили указатель на параметр k

int s=0;

for(;k!=0;k--)

s+=*(++p);

return s/k;

}

void main()

{

//среднее арифметическое 4+6

cout<<”\n4+6=”<<sum(2,4,6);

//среднее арифметическое 1+2+3+4

cout<<”\n1+2++3+4=”<<sum(4,1,2,3,4);

}

 

Для доступа к списку параметров используется указатель *p типа int. Он устанавливается на начало списка параметров в памяти, а затем перемещается по адресам фактических параметров (++p).

 

/*Найти среднее арифметическое последовательности чисел, если известен признак конца списка параметров */

#include<iostream.h>

int sum(int k, …)

{

int *p = &k; //настроили указатель на параметр k

int s = *p;   //значение первого параметра присвоили s

for(int i=1;p!=0;i++) //пока нет конца списка

s += *(++p);

return s/(i-1);

}

 

void main()

{

//находит среднее арифметическое 4+6

cout<<”\n4+6=”<<sum(4,6,0);

//находит среднее арифметическое 1+2+3+4

cout<<”\n1+2++3+4=”<<sum(1,2,3,4,0);

}

 

Рекурсия

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

Рекурсивное решение задачи состоит из двух этапов:

1. исходная задача сводится к новой задаче, похожей на исходную, но несколько проще;

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

Задача 1. Вычислить факториал (n!), используя рекурсию.

Исходные данные:  n

Результат:  n!

Рассмотрим эту задачу на примере вычисления факториала для n=5. Более простой задачей является вычисление факториала для n=4. Тогда вычисление факториала для n=5 можно записать следующим образом:

5!=4!*5.

Аналогично:

4!=3!*4;

3!=2!*3;

2!=1!*2 ;

1!=0!*1

Тривиальная (простая) задача:

0!=1.

Можно построить следующую математическую модель:

 

 

#include <iostream.h>

int fact(int n)

{

if (n==0)return 1;//тривиальная задача

return (n*fact(n-1));

}

}

void main()

{

cout<<"n?";

int k;

cin>>k;//вводим число для вычисления факториала

cout<<k<<"!="<<fact(k);//вычисление и вывод результата

}                                   

 

Задача 2. Вычислить степень, используя рекурсию.

Исходные данные:  x,n

Результат:  xn

Математическая модель:

 

 

#include <iostream.h>

int pow( int x,int n)

{

if(n==0)return 1;//тривиальная задача

return(x*pow(x,n-1));

}

void main()

{

cout<<"n?";

int x;

cin>>x; //вводим число 

int k;

cin>>k; //вводим степень

//вычисление и вывод результата cout<<x<<"^"<<k<<"="<<pow(x,k);

}

Перегрузка функций

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

 

#include <iostream.h>

#include <string.h>

 

//сравнение двух целых чисел

int max(int a, int b)

{

if (a>b) return a;

else return b;

}

 

//сравнение двух вещественных чисел

float max(float a, float b)

{

if(a>b)return a;

else return b;

}

 

//сравнение двух строк

char* max(char* a, char* b)

{

if (strcmp(a,b)>0) return a;

else return b;

}

 

void main()

{

int a1,b1;

float a2, b2;

char s1[20];

char s2[20];

 

cout<<"\nfor int:\n";

cout<<"a=?";cin>>a1;

cout<<"b=?";cin>>b1;

cout<<"\nMAX="<<max(a1,b1)<<"\n";

 

cout<<"\nfor float:\n";

cout<<"a=?";cin>>a2;

cout<<"b=?";cin>>b2;

cout<<"\nMAX="<<max(a2,b2)<<"\n";

 

cout<<"\nfor char*:\n";

cout<<"a=?";cin>>s1;

cout<<"b=?";cin>>s2;

cout<<"\nMAX="<<max(s1,s2)<<"\n";

}

 

Правила описания перегруженных функций:

· Перегруженные функции должны находиться в одной области видимости.

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

· Функции не могут быть перегружены, если описание их параметров отличается только модификатором const или наличием ссылки: функции int& f1(int&, const int&){…} и int f1(int, int){…} – не являются перегруженными, т. к. компилятор не сможет узнать какая из функций вызывается, потому что нет синтаксических отличий между вызовом функции, которая передает параметр по значению и функции, которая передает параметр по ссылке.

 

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

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

 

template <class имя_типа [,class имя_типа]>

заголовок_функции

{

тело функции

}

 

Таким образом, шаблон семейства функций состоит из 2 частей – заголовка шаблона: template<список параметров шаблона> и обыкновенного определения функции, в котором вместо типа возвращаемого значения и/или типа параметров, записывается имя типа, определенное в заголовке шаблона.

 

/*шаблон функции, которая находит абсолютное значение числа любого типа */

template<class type> //type имя параметризируемого типа

type abs(type x)

{

if(x<0)return -x;

else return x;

}

 

Шаблон служит для автоматического формирования конкретных описаний функций по тем вызовам, которые компилятор обнаруживает в программе. Например, если в программе вызов функции осуществляется как abs(-1.5), то компилятор сформирует определение функции double abs(double x){…}.

 

//шаблон функции, которая меняет местами две переменных

template <class T> //T – имя параметризируемого типа

void change(T* x, T* y)

{

T z=*x;

*x=*y;

*y=z;

}

 

Вызов этой функции может быть:

 

long k=10, l=5;

change(&k, &l);

 

Тогда, компилятор сформирует определение:

 

void change(long* x, long* y)

{

long z=*x;

*x=*y;

*y=z;

}

 

/*шаблон функции, которая находит номер максимального элемента в массиве*/

#include<iostream.h>

template<class Data>

Data& rmax(int n, Data a[])

{

int im=0;

for(int i=0;i<n;i++)

if(a[im]<a[im]) im=i;

return d[im]; //возвращает ссылку

}

 

void main()

{

int n=5;

int x[]={10,20,30,15};

//найти номер максимального элемента в массиве целых чисел

cout<<”\nrmax(n,x)=”<<rmax(n,x)<<”\n”;

rmax(n,x)=0; //заменить максимальный элемент на 0

for(int i=0;i<n;i++)   //вывод массива

cout<<x[i]<<” ”;

cout<<”\n”;

//следующий массив

float y[]={10.4,20.2,30.6,15.5};

/*найти номер максимального элемента в массиве вещественных чисел*/

cout<<”\nrmax(n,y)=”<<rmax(n,y)<<”\n”;

rmax(4,y)=0; //заменить максимальный элемент на 0

for(in i=0;i<n;i++)    //вывод массива

cout<<y[i]<<” ”;

cout<<”\n”;

}

 

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

rmax(n,x)=30

10 20 0 15

rmax(n,y)=30.6

10.4 20.2 0 15.5

Основные свойства параметров шаблона функций

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

· Список параметров шаблона не может быть пустым.

· В списке параметров шаблона может быть несколько параметров, каждый из них начинается со слова class.

 

Указатель на функцию

 

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

Указатель на функцию определяется следующим образом:

 

тип_функции(*имя_указателя)(спецификация параметров)

 

int f1(char c){…} //определение функции

int(*ptrf1)(char); //определение указателя на функцию f1

char* f2(int k,char c){…}   //определение функции

char* ptrf2(int,char); //определение указателя на функцию f2

 

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

Вызов функции с помощью указателя имеет вид:

 

(*имя_указателя)(список фактических параметров);

 

#include <iostream.h>

void f1()

{

cout<<”\nfunction f1”;

}

 

void f2()

{

cout<<”\nfunction f2”;

}

 

void main()

{

void(*ptr)(); //указатель на функцию

ptr=f2;  //указателю присваивается адрес функции f2

(*ptr)(); //вызов функции f2

ptr=f1;  //указателю присваивается адрес функции f1

(*ptr)(); //вызов функции f1с помощью указателя

}

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

void (*ptr)()=f1;

Указатели н функции могут быть объединены в массивы. Например, float(*ptrMas[4])(char) – описание массива, который содержит 4 указателя на функции. Каждая функция имеет параметр типа char и возвращает значение типа float. 

Обратиться к такой функции можно следующим образом:

 

float a=(*ptrMas[1])(’f’); //обращение ко второй функции

 

#include <iostream.h>

#include <stdlib.h>

 

void f1()

{

cout<<"\nThe end of work";exit(0);

}

 

void f2()

{

cout<<"\nThe work #1";

}

 

void f3()

{

cout<<"\nThe work #2";

}

 

void main()

{

void(*fptr[])()={f1,f2,f3};//массив и указателей на функции

int n;

while(1)           //бесконечный цикл

{

cout<<"\n Enter the number";

cin>>n;

fptr[n]();//вызов функции с номером n

}

}

 

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

 

#include <iostream.h>

#include <math.h>

 

typedef float(*fptr)(float);//тип-указатель на функцию уравнения

 

/*решение уравнения методом половинного деления, уравнение передается с помощью указателя на функцию */

float root(fptr f, float a, float b, float e)

{

float x;

do

{

x=(a+b)/2;         //находим середину отрезка

if ((*f)(a)*f(x)<0)    //выбираем отрезок

b=x;

else a=x;

}

while((*f)(x)>e && fabs(a-b)>e);

return x;

}

 

//функция, для которой ищется корень

float testf(float x)

{

return x*x-1;

}

 

void main()

{

/*в функцию root передается указатель на функцию, координаты отрезка и точность */

float res=root(testf,0,2,0.0001);

cout<<”\nX=”<<res;

}

 

Ссылки на функцию

 

Подобно указателю на функцию определяется и ссылка на функцию:

 

тип_функции(&имя_ссылки)(параметры)

инициализирующее_выражение;

 

int f(float a,int b){…} //определение функции

int (&fref)(float,int)=f; //определение ссылки

 

Использование имени функции без параметров и скобок будет восприниматься как адрес функции. Ссылка на функцию является синонимом имени функции. Изменить значение ссылки на функцию нельзя, поэтому более широко используются указатели на функции, а не ссылки.

 

#include <iostream.h>

 

void f(char c)

{

cout<<”\n”<<c;

}

 

void main()

{

void (*pf)(char); //указатель на функцию

void (&rf)(char); //ссылка на функцию

f(’A’); //вызов по имени

pf=f; //указатель ставится на функцию

(*pf)( ’B’); //вызов с помощью указателя

rf(’C’); //вызов по ссылке

}


Дата добавления: 2018-04-04; просмотров: 161;