11832

Перегрузка функций. Шаблоны функций

Лабораторная работа

Информатика, кибернетика и программирование

Лабораторная работа №12. Перегрузка функций. Шаблоны функций 1 Цель и порядок работы Цель работы – ознакомиться с возможностью перегрузки функций и научиться применять полученные знания на практике. Научиться использовать шаблоны функции и функции с переменным количе...

Русский

2013-04-12

152.5 KB

32 чел.


Лабораторная работа №12. Перегрузка функций. Шаблоны функций

1 Цель и порядок работы

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

Порядок выполнения работы:

  •  ознакомиться с описанием лабораторной работы;
  •  получить задание у преподавателя, согласно своему варианту;
  •  написать программу и отладить ее на ЭВМ;
  •  оформить отчет.

2 Краткая теория

Каждая программа на C++ – это совокупность функций, каждая из которых должна быть определена или описана до её использования в конкретном модуле программы. Рассмотрим более сложные примеры использования функций.

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

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

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

Небольшие перегруженные функции удобно применять при отладке программ.

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

void print(char *str, const int i, const int j)

{

  cout << str << '|' << oct << setw(4) << i << '|' << setw(4) << j

     << '|' << endl; 

}

void print(float array[], const int n)

{

  cout << "Массив:" << endl; 

  cout.setf(ios::fixed); 

  cout.precision(2);

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

  {

      cout << array[i] << " "; 

      if ((i + 1) % 4 == 0) cout << endl;

  }

  cout << endl;

}

void print(Man m)

{

  cout.setf(ios::fixed);

  cout.precision(2);

  cout << setw(40) << m.name << ' ' << m.birthday << ' '

     << m.pay << endl;

}

В первой из этих функций на экран выводятся строка и два целых числа в восьмеричной форме, разделенных вертикальными черточками для читаемости. Под каждое число отводится по 4 позиции (действие манипулятора setw распространяется только на ближайшее выводимое поле).

Во второй функции для вывода вещественных значений по четыре числа на строке задается вид вывода с фиксированной точкой и точностью в два десятичных знака после запятой. Для этого используются методы установки флагов setf, установки точности precision и константа fixed, определенная в классе ios. Точность касается только вещественных чисел, ее действие продолжается до следующей установки. Третья функция выводит поля знакомой нам по шестому семинару структуры так, чтобы они не склеивались между собой. Манипулятор setw устанавливает ширину следующего за ним поля. Это приведет к тому, что фамилии будут выведены с отступом от края экрана. Вызов этих функций в программе может выглядеть, например, так:

print("После цикла ", i, j);

print(a, n);

print(m);

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

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

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

Неоднозначность может также возникнуть из-за параметров по умолчанию и ссылок. Рассмотрим создание перегруженных функций на примере.

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

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

#include "stdafx.h"

#include <iostream>

#include <math.h>

using namespace std;

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

//в декартовой системе

struct cartesian{

  double x, y;

};

//и в по системе координат

struct polar{

  double r, pi;

};

//теперь определим перегружаемую функцию,

//принимающую координаты двух точек через полярные координаты

double len(polar a, polar b)

{

  cout << "Считаем расстояние через полярные координаты" << endl;

  return sqrt(pow(a.r, 2) + pow(b.r, 2) - 2*a.r*b.r*cos(a.pi - b.pi));

}

//а затем принимающую координаты двух точек через декартовы координаты

double len(cartesian x, cartesian y)

{

  cout << "Считаем расстояние через декартовы координаты" << endl;

  return sqrt(pow(y.x - x.x, 2)+pow(y.y - x.y, 2));

}

//будем считать, что при передаче четырех параметров

//передаются декартовы координаты двух точек

double len(double x1, double y1, double x2, double y2)

{

  cout << "Считаем расстояние через декартовы \

     координаты с 4-мя параметрами" << endl;

  return sqrt(pow(x2 - x1, 2)+pow(y2 - y1, 2));

}

void main(int argc, char* argv[])

{

  setlocale(LC_ALL, "Russian");

  const double PI = 3.14159;

  cartesian a = {3, 0},

            b = {1, 1};

  polar c = {1.41, PI/4},

        d = {3.1, 0.95};

  double x1 = 1.4, y1 = 2.5,

         x2 = 2.1, y2 = 3.7;

  cout << len(a, b) << endl;

  cout << len(c, d) << endl;

  cout << len(x1, y1, x2, y2) << endl;

}

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

Считаем расстояние через декартовы координаты

2.23607

Считаем расстояние через полярные координаты

1.7246

Считаем расстояние через декартовы координаты с 4-мя параметрами

1.38924

2.2 Рекурсивные функции

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

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

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

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

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

template <class ttype>

ttype имя_функции (список_формальных_параметров)

{

  тело функции

}

Здесь ttype – любой корректный идентификатор, который автоматически заменяется компилятором на любой стандартный тип.

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

#include "stdafx.h"

#include <iostream>

#include <math.h>

using namespace std;

template <class array_type>

array_type max(array_type *a, const int N)

{

  array_type m = a[0];

  for (int i = 1; i < N; i++)

      if (a[i] > m)

      {   

          m = a[i];

      }

  return m;

}

int main(int argc, char* argv[])

{

  setlocale(LC_ALL, "Russian");

  double a[] = {2.5, 8.3, 6};

  int b[] = {3, 5, -1, 2};

  char c[] = {'A', 'b', 'Z', 'r'};

  cout << max(a, sizeof(a)/sizeof(a[0])) << endl;

  cout << max(b, sizeof(b)/sizeof(b[0])) << endl;

  cout << max(c, sizeof(c)/sizeof(c[0])) << endl;

  return 0;

}

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

Основные свойства параметров шаблона:

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

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

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

Допустимый заголовок шаблона:

template <class type1, class type2>

Соответственно, неверен заголовок:

template <class type1, type2, type3>

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

template <class type1, class type1, class type1>

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

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

тип имя (список_явных_параметров, ...);

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

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

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

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

#include "stdafx.h"

#include <iostream>

using namespace std;

long summa(int k, ...)

{

  int *pik = &k;

  

  long total = 0;

  

  for (; k; k--)

      total += *(++pik);

  

  return total;

}

int main(int argc, char* argv[])

{

  setlocale(LC_ALL, "Russian");

  

  cout << "\n summa(2, 6, 4) = " << summa(2, 6, 4);

  cout << "\n summa(6, 1, 2, 3, 4, 5, 6) =" <<

     summa(6, 1, 2, 3, 4, 5, 6);

  

  return 0;

}

Результат

summa(2, 6, 4)=10

summa(6, 1, 2, 3, 4, 5, 6)=21

Особенность этой программы, что указатель pik может работать только с целочисленными фактическими параметрами.

#include "stdafx.h"

#include <iostream>

using namespace std;

double prod(double arg, ...)

{

  double result = 1.0;

  

  double *prt = &arg;

  

  if (*prt == 0.0)

      return 0.0;

  

  for( ; *prt; prt++)

      result *= *prt;

  

  return result;

}

int main(int argc, char* argv[])

{

  setlocale(LC_ALL, "Russian");

  

  cout <<"\n prod(2e0,4e0,3e0,0e0) = " << prod(2e0,4e0,3e0,0e0);

  cout <<"\n prod(1.5,2.0,3.0,0.0) = " << prod(1.5,2.0,3.0,0.0);

  cout <<"\n prod(1.4,3.0,0.0,16.0,84.3,0.0) = " <<

      prod(1.4,3.0,0.0,16.0,84.3,0.0);

  cout << "\n prod(0e0) ="<< prod(0e0);

  

  return 0;

}

Результат

prod(2e0,4e0,3e0,0e0) = 24

prod(1.5,2.0,3.0,0.0) = 9

prod(1.4,3.0,0.0,16.0,84.3,0.0) = 4.2

prod(0e0) = 0

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

3 Контрольные вопросы

  1.  Что такое перегрузка функции?
  2.  Для чего применяется перегрузка функций?
  3.  Как определяется, какая из версий перегружаемых функций будет вызвана?
  4.  Каким образом описывается шаблон семейства функций?
  5.  Что такое рекурсия?
  6.  Какие виды рекурсии вы знаете?
  7.  Как описываются функции с переменным количеством параметром?

4 Задание

  1.  Написать программу в соответствии с вариантом задания из пункта 5.1.
  2.  Отладить и протестировать программу.
  3.  Написать программу в соответствии с вариантом задания из пункта 5.2.
  4.  Отладить и протестировать программу.
  5.  Оформить отчёт.

5 Варианты заданий

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

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

Два параметра типа int.

Три параметра типа int.

Два параметра типа float.

Три параметра типа double.

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

Структура «дата» (год, месяц, день).

Три целочисленных параметра: год, месяц, день.

Два целочисленных параметра: месяц, день (считать передаваемые числа датой текущего года).

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

Два параметра типа int.

Два параметра типа long.

Два параметра типа float.

Два параметра типа double.

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

Одномерный массив типа int размерностью N.

Одномерный массив типа float размерностью N.

Одномерный массив типа double размерностью N.

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

Три параметра типа int.

Четыре параметра типа int.

Три параметра типа float.

Два параметра типа double.

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

Структура «дата» (год, месяц, день).

Три целочисленных параметра: год, месяц, день.

Два целочисленных параметра: месяц, день (считать передаваемые числа датой текущего года).

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

Структура «дата» (год, месяц, день).

Три целочисленных параметра: год, месяц, день.

Два целочисленных параметра: месяц, день (считать передаваемые числа датой текущего года).

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

Два параметра типа int.

Два параметра типа long.

Два параметра типа float.

Два параметра типа double.

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

Структура «время» (часы, минуты, секунды).

Три целочисленных параметра: часы, минуты, секунды.

Два целочисленных параметра: часы, минуты.

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

Структура «время» (часы, минуты, секунды).

Три целочисленных параметра: часы, минуты, секунды.

Два целочисленных параметра: часы, минуты.

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

Одномерный массив типа int размерностью N.

Одномерный массив типа float размерностью N.

Одномерный массив типа double размерностью N.

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

Структура «дата» (год, месяц, день).

Три целочисленных параметра: год, месяц, день.

Два целочисленных параметра: месяц, день (считать передаваемые числа датой текущего года).

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

Два параметра типа структура «точка» (координаты x, y).

Четыре параметра типа float.

Четыре параметра типа double.

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

Структура «время» (часы, минуты, секунды).

Три целочисленных параметра: часы, минуты, секунды.

Два целочисленных параметра: часы, минуты.

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

Одномерный массив типа int размерностью N.

Одномерный массив типа float размерностью N.

Одномерный массив типа double размерностью N.

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

Два параметра типа структура «время» (часы, минуты, секунды).

Шесть целочисленных параметра: часы, минуты, секунды.

Четыре целочисленных параметра: часы, минуты.

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

Два параметра типа int.

Три параметра типа int.

Два параметра типа float.

Три параметра типа float.

Три параметра типа double.

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

Структура «дата» (год, месяц, день).

Три целочисленных параметра: год, месяц, день.

Два целочисленных параметра: месяц, день (считать передаваемые числа датой текущего года).

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

Два параметра: x и n – оба типа int.

Два параметра: x и n – оба типа float.

Два параметра: x – типа float, и n – типа int.

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

Одномерный массив типа int размерностью N.

Одномерный массив типа float размерностью N.

Одномерный массив типа double размерностью N.

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

Структура «время» (часы, минуты, секунды).

Три целочисленных параметра: часы, минуты, секунды.

Два целочисленных параметра: часы, минуты.

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

Два параметра типа int.

Три параметра типа int.

Три параметра типа float.

Два параметра типа double.

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

Структура «дата» (год, месяц, день).

Три целочисленных параметра: год, месяц, день.

Два целочисленных параметра: месяц, день (считать передаваемые числа датой текущего года).

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

Одномерный массив типа int размерностью N.

Одномерный массив типа float размерностью N.

Одномерный массив типа double размерностью N.

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

Структура «дата» (год, месяц, день).

Три целочисленных параметра: год, месяц, день.

Два целочисленных параметра: месяц, день (считать передаваемые числа датой текущего года).

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

На основе задание 5.1 построить шаблон семейства функций. Вариант выбирать путем добавления 3 к номеру в журнале.

6 Содержание отчета

  1.  Титульный лист.
  2.  Наименование и цель работы.
  3.  Краткое теоретическое описание.
  4.  Задание на лабораторную работу.
  5.  Схема алгоритма.
  6.  Листинг программы.
  7.  Результаты выполнения программы.


 

А также другие работы, которые могут Вас заинтересовать

29916. Организация и пути повышения эффективности государственного финансового контроля 12.7 KB
  Организация и пути повышения эффективности государственного финансового контроля. Организация и функционирование эффективной системы финансового контроля обязательный и непременный элемент государственной финансовой политики. Сегодня назрела необходимость должного правового регулирования организации и деятельности органов государственного контроля так как контрольная деятельность в России пока малоэффективна. В связи с этим необходимо дальнейшее совершенствование действующего...
29917. Формы и методы государственного регулирования инвестиционной и инновационной деятельности. Их особенности в Российской Федерации 16.64 KB
  Формы и методы государственного регулирования инвестиционной и инновационной деятельности. Осуществление государственного регулирования инвестиционной деятельности на федеральном уровне невозможно без учета потребностей регионов России необходимости их пропорционального развития. Регулирование инвестиционной деятельности в регионах является частью экономической политики государства в целом и каждого из регионов в отдельности. В настоящее время выделяют следующие методы государственного регулирования инвестиционной деятельности: Прямые методы...
29918. Особенности аудита связанных сторон 67 KB
  Вместе с тем операции которые компания осуществляет с такими лицами могут отличаться от остальных операций. Операциями со связанной стороной могут быть: приобретение и продажа товаров работ услуг; приобретение и продажа основных средств и других активов; аренда имущества и предоставление имущества в аренду; финансовые операции включая предоставление займов; передача в виде вклада в уставные складочные капиталы; предоставление и получение обеспечений исполнения обязательств; другие операции. Тем не менее не следует ожидать...
29919. Особенности организации внутреннего и внешнего аудита 28.5 KB
  Остановимся на каждом из них более подробно: Внешний аудит – это независимая и комплексная проверка финансовой бухгалтерской отчетности. Внешний аудит проводится только на основе договора который заключается с аудиторской организацией. Внешний аудит относится к обязательным проверкам а вот аудит внутренний обычно проводится только по инициативе руководителей или акционеров.
29922. Отличие аудита от других форм эк.контроля 34 KB
  По мнению абсолютного большинства специалистов первое место принадлежит ревизии. Цель ревизии определение законности полноты и своевременности взаимных платежей и расчетов проверяемого объекта и федерального бюджета бюджетов государственных внебюджетных фондов а также эффективности и целевого использования государственных средств. Объекты ревизии все государственные органы в том числе их аппараты и учреждения в Российской Федерации государственные внебюджетные фонды а также органы местного самоуправления...
29923. Оформление результатов аудиторской проверки 35.5 KB
  Аудиторское заключение официальный документ дающий оценку достоверности бухгалтерского учета и отчетности аудируемого предприятия подтвержденный подписью имеющего лицензию руководителя проверяющей группы аудиторской фирмы и печатью этой фирмы. Возможны четыре вида аудиторских заключений: заключение без замечаний безоговорочное заключение; заключение с замечаниями заключение с оговорками; отрицательное заключение; заключение не дается совсем либо дается отказное заключение. Заключение с замечаниями делается при выявлении...
29924. Оценка финансового состояния, платеже- и кредитоспособности организации 31.5 KB
  Оценка платежеспособности осуществляется на основе характеристики ликвидности текущих активов т. Понятия платежеспособности и ликвидности очень близки но второе более емкое. От степени ликвидности баланса зависит платежеспособность. Анализ ликвидности баланса заключается в сравнении средств по активу сгруппированных по степени убывающей ликвидности с обязательствами по пассиву которые сгруппированы по степени срочности их погашения.