- •Содержание
- •Предисловие
- •1. Общие методические указания
- •2. Рабочая программа и методические указания к темам курса
- •2.1 Типы данных
- •2.2.1 Операторы управления
- •If (выражание) оператор_1;
- •2.2.2 Операторы ввода-вывода
- •Контрольные вопросы
- •2.3 Массивы и указатели
- •2.3.1 Указатели
- •2.3.2 Массивы
- •Контрольные вопросы
- •2.4 Типы данных, определяемые пользователем
- •2.4.1 Структуры
- •2.4.2 Перечисления
- •2.4.3 Структуры с битовыми полями
- •2.4.4 Объединение (смесь)
- •Контрольные вопросы
- •2.5 Функции
- •2.5.1 Общие сведения о функциях
- •Способы передачи параметров функции
- •2.5.2 Рекурсивные функции
- •2.5.3 Перегрузка функций
- •2.5.4 Шаблоны функций
- •Контрольные вопросы
- •3. Перечень лабораторных работ
- •Список литературы
- •Давыдов, в.Г. Программирование и основы алгоритмизации [Текст]: учеб. Пособие/ в.Г. Давыдов – м: Высшая школа, 2003. – 447с.: ил.: 22 см. – Библиогр.: с. 442. – 3000 экз. - isbn 5-060-04432-7.
2.5.2 Рекурсивные функции
В языке C++ допустима рекурсия. Рекурсия это способ организации вычислительного процесса, при котором процедура или функция может обращаться сама к себе. Покажем рекурсивную реализацию метода быстрой сортировки. В методе используется процедура половинного разделения, применяемая на 1-ом шаге ко всему массиву, а на следующих шагах – к его фрагменту. На каждом шаге образуются две половинки текущего фрагмента, к которым снова применяется процедура разделения. Если массив сортируется по возрастанию, то в левую половинку записываются меньшие значения, а в правую – большие (если по убыванию, то наоборот).
Одну из возможных версий программы покажем на примере:
#include<iostream.h>
void quicksort(float* arr, int left,int right);
void main()
{const int n = 10;
float ar[n];
int i, l, r;
// cputs(«введите данные о значениях исходного массива»);
for(i=0; i<n; i++){
cout<<”введите а[“<<k<<”] исходного массива \n”;
cin>>ar[i];
}
l = 0; r = n – 1; // левая и правая границы начального
// фрагмента
quicksort(ar, l, n); // вызов функции
for(i=0; i<n; i++)
printf(“ar[ %d ]= %d \n”, i, ar[i]); // cout<<ar[i]<<” “;
}
void quicksort(float * arr, int left, int right)
{int i = left, j = right; //левая и правая границы фрагмента
float middle = arr[(left + right) / 2];
float temp;
while (i < j) {
while (arr[i] < middle) i++;
while (middle < arr[j]) j--;
if (i <= j) { // замена значений
temp = arr[i];
arr[i] = arr[j];
arr[j] = temp;
i++;
j--;
}
}
if(i < right)quicksort(arr, i, right); /* вызов функции для
сортировки правой половины фрагмента массива */
if(left < j)quicksort(arr, left, j); /* для сортировки левой
половины фрагмента */
}
Процедура разделения реализована в виде рекурсивно вызываемой функции quicksort(), в теле которой есть два обращения к самой себе: для сортировки левой половинки теккущего фрагмента и сортировки его правой половинки.
Однако, у рекурсии есть недостатки:
такую программу труднее отлаживать, поскольку требуется контролировать глубину рекурсивного обращения;
при большой глубине стек может переполниться;
использование рекурсии повышает накладные расходы (в данном случае в стеке сохраняются не два числа, представляющие собой границы фрагмента, а гораздо больше, не говоря уже о затратах, связанных с вызовом функции).
Поэтому рекурсию следует применять с осторожностью.
2.5.3 Перегрузка функций
Часто бывает удобно, чтобы функции, реализующие один и тот же алгоритм для различных типов данных, имели одно и то же имя. Использование нескольких функций с одним и тем же именем, но с различными типами параметров, называется перегрузкой функций (перегружаемыми функциями).
Компилятор определяет, какую именно функцию требуется вызвать, по типу фактических параметров. Этот процесс называется разрешением перегрузки. Тип возвращаемого функцией значения в разрешении не участвует.
int sum(int a, int b)
{return (a+b);}
double sum(double a, double b)
{return(a+b);}
double sum(double a, double b, double c)
{return(a+b+c);}
Приведенные выше функции отличаются друг от друга следующим образом: первая от второй типом формальных параметров и типом возвращаемого результата; первая от третьей количеством и типом формальных параметров и типом возвращаемого результата; вторая от третьей количеством формальных параметров.
Если точного соответствия списков параметров объявления функции и в ее вызове не найдено, выполняются преобразования типов в соответствии с общими правилами, например, bool и char в int, float в double и т.п. Далее выполняются стандартные преобразования типов, например, int в double или указателей в void*. Следующим шагом является выполнение преобразований типа, заданных пользователем, а также поиск соответствий за счет переменного числа аргументов функций. Если соответствие на одном и том же этапе может быть получено более чем одним способом, вызов считается неоднозначным и выдается сообщение об ошибке.
Существует ряд правил описания перегруженных функций:
перегруженные функции должны находиться в одной области видимости, иначе произойдет сокрытие аналогично одинаковым именам переменных во вложенных блоках (доступ осущствляется только к одной из них);
перегруженные функции могут иметь параметры по умолчанию, при этом значения одного и того же параметра в разных функциях должны совпадать. В различных вариантах перегруженных функций может быть различное число параметров по умолчанию;
функции не могут быть перегружены, если описание их параметров совпадает или отличается только модификатором const или использованием ссылки (например, int и const int, int и int&).