§9 Функции. Раздельная компиляция

Функция в программе

В больших программах часто появляются фрагменты кода, в которых выполняются идентичные операции. С одной стороны это увеличивает размер программы, а с другой стороны появляется желание выделить эти фрагменты в обособленный алгоритм, чтобы обращаться к нему по мере необходимости. Этот подход (парадигма), который называется процедурным программированием, реализуется с помощью функций.
Для того, чтобы использовать функцию её нужно объявить, определить и вызвать. Функция может возвращать или не возвращать значение, т. е. результат работы функции. Если функция не возвращает никакого значения, то такую функцию называют процедурой. В С++ процедуры (в отличие от некоторых других языков программирования) также называются функциями. Функция возвращающая значение должна иметь тип возвращаемого значения. Функции, являющиеся членами класса называются методами. В C++ реализовано большое количество встроенных функций. Примером тому может служить функции библиотеки cmath, реализующие различные математические функции, которые мы ранее уже использовали. В целом, стандартная библиотека C++ (STD) содержит несколько десятков библиотек в которых реализованы решения различных практических задач в виде функций. Помимо библиотек STD разработчик может воспользоваться и сторонними библиотеками. Этот богатый библиотечный набор программист всегда может использовать при разработке своих собственных функций.
Для начала работы с функцией её нужно объявить. Объявление функции иначе называется прототипом. Объявление функции должно находиться вне тела главной функции (main) в самом начале файла программы. Обычно прототипы размещаются после директив include. Определение (т. е. реализация алгоритма) функции также выносится за пределы главной функции, но уже в нижней части файла программы. Рассмотрим как осуществляется работа с функцией в программе на примере использования функции определения площади прямоугольного треугольника по двум катетам. Назовём функцию именем area. Имя функции (идентификатор) должно подчиняться общим правилам для идентификаторов.

Функции возвращающие значение

Функция area является функцией двух аргументов. Тип входящих данных и возвращаемый тип, в данном случае, имеют одинаковый тип double. Это отражается в прототипе:

double area(double, double);

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

double area(double x, double y) {
    return x * y / 2;
}

В определении функции переменные x и y являются параметрами функции. Типы параметров, их последовательность и количество должны в точности соответствовать тому порядку который указан в прототипе (а также в инструкции вызова функции). Для возврата значения функции используется инструкция return, которая принимает выражение для определения возвращаемого значения. Вызов функции в программе выглядит следующим образом:

area(a, b);

Обратите внимание на то, что при вызове функции типы данных не указываются. Переменные a и b называются аргументами функции. Аргументы функции являются инициализаторами параметров. Переменные x и y — это локальные переменные, они не доступны в основной программе. Чтобы в программе не было путаницы с именами, принято чтобы имена локальных параметров и других переменных функции не совпадали с именами переменных в основной программе или именами глобальных объектов. Объединим эти части в один исходный код:
Программа 9.1

#include <iostream>
using namespace std;

double area(double, double);

int main() {
    double a, b;
    cout << "a = "; cin >> a;
    cout << "b = "; cin >> b;
    double S = area(a, b);
    cout << "S = " << S << endl;
    return 0;
}

double area(double x, double y) {
    return x * y / 2;
}

Функции возвращающие значение должны возвращать это значение в точку вызова — либо для присваивания нового значения переменной (или инициализируя её), либо в поток. Существуют и другие способы использования возвращаемого значения, но их мы обсудим позднее.
В функциях не используется более одного выражения с return. Однако можно использовать несколько return, если они находятся в ветвях условной инструкции. Например:

if (x > y)
    return (x + y) / 2;
else
    return sqrt(x * y);

Функции не возвращающие значение

Функции не возвращающие значения в качестве спецификатора типа используют специальный тип void. Познакомимся с такой функцией на примере функции печатающей на экране линию из символов "-", длинною 30 символов.
Программа 9.2

#include <iostream>
using namespace std;

void line(void);

int main()
{
    int n;
    cout << "Сколько напечатать линий? -> ";
    cin >> n;
    while (n--) {
        line();
    }
    return 0;
}

void line() {
    for (int i = 0; i < 30; i++) {
        cout << "-";
    }
    cout << endl;
}

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

#include <iostream>
using namespace std;

void mySwap(int&, int&);

int main() {
	int a, b;
	cout << "a = "; cin >> a;
	cout << "b = "; cin >> b;
	mySwap(a, b);
	cout << "Значения переменных после обмена:\n"
		 << "a = " << a << endl
		 << "b = " << b << endl;
	return 0;
}

void mySwap(int &x, int &y) {
	if (x == y)
		return;
	int c = x;
	x = y;
	y = c
}

Передача аргументов по значению и по ссылке

В программе 9.3, в отличие от программы 9.1, параметрами функции являются ссылки. В функцию программы 9.1 передаются не сами переменные, а копии значений переменных a и b. Такая форма передачи параметров называется передачей по значению. Если параметрами функции являются ссылки или указатели, то при работе с переменными внутри функции изменяться будут не их копии, а сами объекты на которые ссылаются ссылки или указуют указатели. Такая форма передачи называется передачей по ссылке или указателю. Одновременно аргументы могут передавать и по ссылке, и по указателю, и по значению.
Поскольку в программе 9.3 параметры передаются по ссылке, то произойдет обмен переменных a и b, а не их копий. Если в функциях возвращающих значение необходимо возвращать более одного значения, то эти значения возвращаются посредством использования передачи по ссылке.

Раздельная компиляция

При проектировании приложения большую программу на C++ можно разделить на несколько файлов. Например, в один файл будут вынесены функции, в другом файле будут вызываться эти функции. Такое приложение будет проще проектировать и сопровождать. Один (или более) файл проекта должен иметь расширение .h, которое говорит о том, что это заголовочный файл.
Заголовочный файл содержит следующие объявления:

  • константы
  • прототипы
  • объявление классов и структур
  • объявление шаблонов
  • встроенные функции

Главным файлом проекта является файл, в котором находится главная функция main(). Чаще всего такой файл имеет имя main.cpp (но это не обязательно). Именно из функции main() запускается работа всей программы. В другие файлы выносятся функции вызываемые из main и из других функций. Создадим небольшой проект «Chisla». В этой программе вводятся два числа. Необходимо определить и вывести следующие сведения, которые будут реализованы в виде функций:

  1. среднее арифметическое(a, b)
  2. среднее геометрическое(a, b)
  3. НОД(a, b)
  4. равенство(a, b)
  5. максимальное(a, b)

В заголовочном файле (назовем его chisla.h) перечислим прототипы функций, которые мы будем использовать в программе:
Программа 9.4a

#ifndef CHISLA_H_
#define CHISLA_H_
double average(int, int);
double geomean(int, int);
int GCD(int, int);
bool equal(int, int); 
int max(int, int);
#endif

Обратите внимание на директивы компилятора #ifndef и #endif. Они используются для того, чтобы избежать повторного включения заголовочного файла и ошибок компиляции.
Сами функции мы определим в файле с именем chisla.cpp. Этот файл должен включать заголовочный файл chisla.h и заголовочные файлы всех используемых библиотек (в данном случае только одна — cmath):
Программа 9.4b

#include <cmath>
#include "chisla.h"

double average(int x, int y) {
	return double(x + y) / 2;
}
double geomean(int x, int y) {
	return sqrt(x * y);
}
int GCD(int x, int y) {
	while(x != y) {
	        if (x > y)
	            x = x - y;
	        else
	            y = y - x;
	    }
	    return x;
}
bool equal(int x, int y) {
	return x == y;
}
int max(int x, int y) {
	return x > y ? x : y;
}

Перейдем к файлу main.cpp. Реализация программного кода в этом файле могла бы выглядеть следующим образом:
Программа 9.4c

#include <iostream>
#include "chisla.h"
using namespace std;

int main() {
	int a, b;
	cout << "a = "; cin >> a;
	cout << "b = "; cin >> b;
	if(!equal(a, b))
	cout << "Среднее арифметическое => "
		 << average(a, b)
		 << "\nСреднее геометрическое => "
		 << geomean(a, b)
		 << "\nМаксимальное => "
		 << max(a, b)
		 << endl
		 << "НОД = "
		 << GCD(a, b)
		 << endl;
	else
		cout << "Числа равны" << endl;
	return 0;
}


Comments are closed