Ссылки как аргументы функции
Теперь можно перейти ко второму из двух механизмов передачи аргументов в функцию. Спецификация параметра функции в виде ссылки изменяет метод передачи параметра. Этот метод не передает аргумент по значению, когда он копируется перед тем, как быть переданным в функцию, а передается по ссылке – то есть параметр выступает псевдонимом передаваемого аргумента. Это исключает всякое копирование и обеспечивает функции прямой доступ к аргументу. Это также означает, что нет необходимости в разыменовании, которое необходимо при передаче указателей на значения.
Опять модифицируем поиск корней квадратного уравнения.
#include <iostream>
#include <math.h>
#include<iomanip>
using namespace std;
int korny_2D(double ,double ,double ,double &,double &);
int korny_2D(double a,double b,double c,double &x1,double &x2)
{
double d;
d=b*b-4.*a*c;
if(d<0.) return 0;
else
if(d==0.)
{
x1=x2=(-b+sqrt(d))/(2.*a);
return 1;
}
else
{
x1=(-b+sqrt(d))/(2.*a);
x2=(-b-sqrt(d))/(2.*a);
return 2;
}
}
int main(void)
{
double a,b,c,x1,x2;
int k;
cout<<"Vvedite kooficienty a*x^2+b*x+c+0\n";
cin>>a>>b>>c;
k=korny_2D(a,b,c,x1,x2);
cout.setf(ios::showpos);
cout<<a<<"*x^2"<<b<<"*x"<<c<<"=0\n";
switch(k)
{
case 0: cout<<"korney net\n";break;
case 1: cout<<"koren 1=n"<<x1<<"\n";break;
case 2: cout<<"2 korny\n"<<"koren 1="<<x1<<"koren 2="<<x2<<endl;break;
}
return 0;
}
Измениения опять отмечены выделением.
Вывод результата не отличается от предыдущего примера.
Вывод доказывает, что функция korny_2D() непосредственно модифицирует переменную, переданную ей в аргументе.
Но если вместо переменной использовать константу в качестве аргумента korny_2D(), то компилятор выдаст сообщение об ошибке. Дело в том, что компилятор обнаруживает, что параметр-ссылка может быть модифицирован внутри функции, но изменять значения констант нельзя. Это может внести в программы некоторое излишнее волнение, без которого имеет смысл обойтись.
С точки зрения оптимизации работы программы по времени и количеству занимаемой памяти передача по ссылке более выгодна, так как не тратится время и место на дублирование данных в памяти, следовательно, функция со ссылками работает быстрее. К тому же можно защитить данные переданные по ссылке от изменения при помощи модификатора const.
Чтобы сообщить компилятору, что программы не собирается никоим образом модифицировать параметр функции, можно применить модификатор const перед спецификацией ссылки. Это заставит компилятор проверить код функции на предмет того, действительно ли он не изменяет значения аргумента, и если нет, то никаких сообщений об ошибках не выдается при использовании константного аргумента.
Когда функция содержит вызов самой себя, такая функция называется рекурсивной. Рекурсивный вызов функции может быть непрямым, когда функция fun1 вызывает функцию fun2, которая в свою очередь, вызывает fun1.
Может показаться, что рекурсия – прямой путь к бесконечному циклу, и если программист будет невнимателен, так и случится. Бесконечный цикл заблокирует компьютер и потребует нажатия комбинации клавиш Ctrl+Alt+Del, чтобы прервать программу, а это всегда неприятно. Предпосылкой избегания бесконечных циклов является наличие в функции некоторого способа, прерывающего процесс.
Если не приходилось сталкиваться с этой техникой ранее, то случаи, когда может пригодиться рекурсия, не слишком очевидны. Однако в физике и математике есть много вещей, которые для своего описания или управления предполагают применение рекурсии. Простой пример – факториал целого числа, который для некоторого заданного N равен произведению 1x2x3x...xN. Этот пример можно рассмотреть в качестве учебного.
#include <iostream>
using namespace std;
long faktorial(int N);
long faktorial(int N)
{
if(N>0) return (N*faktorial(N-1));
else return 1;
}
int main(void)
{
long f;
int n;
cout<<"Vvedite n :\t";
cin>>n;
f=faktorial(n);
cout<<n<<"!="<<f<<endl;
return 0;
}
Вывод этой программы будет таким (рис. 56):
Рис. 56. Результат работы программы подсчета факториала
Собственно рекурсия реализована в выражении (N*faktorial(N-1)); Здесь используется свойство факториала N!=N*(N-1)! .
В случае если N равно нулю, функция возвращает 1, в других случаях возвращает результат вычисления выражения N*faktorial(N-1), то есть функция faktorial() вызывается еще раз с аргументом на 1 меньше. Таким образом, ветвь else оператора if обеспечивает механизм, необходимый для прерывания бесконечной последовательности рекурсивных вызовов функции.
Понятно, что если внутри функции faktorial() значение N больше нуля, то выполняется следующий вызов faktorial(). Фактически для любого заданного значения N, большего 0, функция вызывается N раз. Этот механизм проиллюстрирован на рис. 57, где предполагается, что аргумент – равен 3.
Как видно, функция faktorial() вызывается четыре раза, чтобы сгенерировать 3!, из которых три вызова рекурсивны, то есть, функция три раза вызывает саму себя.
Если только программист не имеет дело с проблемой, которая сама по себе диктует необходимость использования рекурсивных функций, или существуют очевидные альтернативы, то лучше применить другой подход, например, цикл. Это намного более эффективно, чем рекурсивные вызовы функций. Если проанализировать рис. То можно увидеть что для того что бы вычислить 1*2*3 в оперативной памяти создается 4 экземпляра функции. При каждом вызове компилятор генерирует копию аргумента функции и отслеживает место в памяти, куда нужно вернуть значение, при каждом выполнении return. Кроме того, ему необходимо сохранить содержимое различных регистров компьютера, чтобы они использовались внутри функции faktorial(), и конечно, восстанавливать их значение при каждом возврате. При сравнительно небольшой глубине рекурсивных вызовов накладные расходы будут заметно больше, чем при использовании цикла.
Однако из этого не следует, что надо не использовать рекурсию. Когда проблема для своего решения требует применения рекурсивных вызовов функций, это может оказаться весьма мощной техникой, значительно упрощающей исходный код.
long faktorial(int N); long f; |
faktorial(3) faktorial ß 3×faktorial(2) |
faktorial(2) faktorial ß 2×faktorial(1) |
faktorial(1) faktorial ß 1×faktorial(0) |
faktorial(0) faktorial ß 1 |
f= faktorial(3) |
2 |
1 |
1 |
6 |
Рис. 57. Схема работы программа расчета факториала