Добавил:
Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:

книги / Технологии разработки объектно-ориентированных программ на язык C++. Основы структурного программирования на алгоритмическом языке C++

.pdf
Скачиваний:
4
Добавлен:
12.11.2023
Размер:
3.17 Mб
Скачать

int n = 0;

// В n запишем число, от которого считаем факториал cout << “Введите число : ”;

cin >> n;

n = factor(n);

// Вызываем функцию, а результат пишем в n cout << n << "! = " << n << endl;

// Выводим это на экран system("pause"); return 0;

}

Рис. 10.2. Пример работы функции

Прототипы функций

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

int main () {

 

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

// Пример ошибочного вызова

 

функции

// Функция должна быть объявлена до этого

system (“pause”);

 

return 0;

 

}

 

int summa (int one, int two) {

// А она объявляется

только здесь

 

return one + two; }

// Вернет сумму этих

двух переменных

 

При вызове функции summa() внутри функции main() компилятор не определит ее полное имя. Компилятор не умеет сначала про-

81

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

Таким образом, необходимо проинформировать компилятор о полном имени функции. Для этого нужно использовать прототип функции.

Прототип функции – это функция, в которой отсутствует блок программного кода (тело функции). В прототипе функции находятся следующие элементы:

полное имя функции;

тип возвращаемого значения функции.

int summa (int, int);

// Прототип функции, он содержит имя, тип функции и типы параметров int main () {

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

// Пример правильного

 

 

вызова функции

system

(“pause”);

 

return

0;

 

}

 

 

int summa (int one, int two) {

// Описание функции

return one + two; }

// Возвращение суммы

двух переменных

 

10.2. Глобальные и локальные переменные

Область видимости переменных

Область видимости переменных – это та часть программы, в ко-

торой пользователь может изменять или использовать переменные. Если переменная была создана в каком-либо блоке, то ее областью

видимости будет являться этот блок от его начала (от открывающей скобки «{») и до его конца (до закрывающей скобки «}»), включая все дополнительные блоки, созданные внутри этого блока [7].

Локальные переменные

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

82

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

int one = 1;

{

int two = 3;

}

one = one + two; // Ошибка, переменная two уже не существует

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

int* func () { int an = 9;

return &an; // Ошибка

}

Глобальные переменные

Глобальные переменные – это переменные, описанные вне функций. Они видны во всех функциях, где нет локальных переменных с такими именами.

int glob_one, glob_two; // Глобальные переменные void func () {

/*

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

*/

 

int n = glob_one;

 

glob_one = glob_two;

 

glob_two = glob_one; }

 

int main {

 

cin >> glob_one >> glob_two;

// Ввод значения

func ();

// Вызов функции

cout << glob_one << “ ” << glob_two;

// Вывод глобальных переменных return 0;

}

83

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

Статические переменные

Статическая переменная сохраняет свое значение даже после выхода из блока, в котором она определена, т.е. она создается (и инициализируется) только один раз, а затем сохраняется на протяжении всей программы. Для создания статической переменной используется ключевое слово static [7].

#include <iostream> using namespace std;

// Объявление функции void func() {

// Это функция, которая увеличивает значение переменной на 1

int result = 1;

// Создание переменной для

 

результата

result++;

// Увеличение значения

 

переменной

cout << result << endl;

// Вывод результата

}

 

int main() {

 

setlocale(LC_ALL, "Rus");

// Трижды вызывается одна функция func();

func();

func();

system("pause"); return 0;

}

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

84

Если изменить эту переменную на локальную, то получится следующая программа:

#include <iostream> using namespace std;

// Объявление функции void func() {

// Это функция, которая увеличивает значение переменной

на 1

 

static int n_result = 1;

// Создание переменной

для результата

 

n_result++;

// Увеличение значения

переменной

 

cout << n_result << endl;

// Вывод результата

}

 

int main() {

 

setlocale(LC_ALL, "Rus");

 

// Трижды вызывается одна функция func();

func();

func();

system("pause"); return 0;

}

И при трехкратном вызове функции переменная будет сохранять свое предыдущее значение и увеличиваться на 1, поэтому на экран выведется столбик из чисел, соответственно равных 2, 3 и 4 [7].

10.3. Параметры функции

Основной способ передачи данных происходит через параметры. Существует два способа передачи параметров в функцию: по адресу и по значению.

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

85

void func(int one, int two) { // Пример передачи по значению int a = one;

one = two; two = a;

}

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

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

void func(int* one, int* two) { // Пример передачи по адресу int a = *one;

*one = *two; *two = a;

}

А в данном примере передаются адреса, поэтому изменятся значения переменных, чьи адреса переданы в качестве параметров.

Передача массивов в функцию

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

//В главной функции пустой создается массив

//В функцию передается указатель на его первый элемент и количество элементов

void func (int *mass, int n) {

for (int i = 0; i == n; i++){ // Ввод элементов массива cin >> mass[i]; }

return; }

86

При использовании динамических массивов все происходит точно так же: в функцию передается указатель на первый элемент и из функции можно изменять элементы массива [7].

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

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

тип имя (явные параметры, …) { тело функции;

}

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

Пример:

#include <iostream>

 

using namespace std;

 

// Объявление функции

 

int summ(int m, ...) {

 

int *p = &m;

// Указатель на первый параметр

int sum = m;

/* Это переменная для накопления

 

суммы заданных параметров */

while (p != 0) {

// Суммирование всех параметров

sum = sum + *p;

*p++;

// Переход по указателю на

 

следующий параметр

}

 

}

int main() {

// Вызов функции с разным числом параметров cout << summ(1, 2, 5, 6) << endl;

87

cout << summ(3, 5) << endl; system("pause");

return 0;

}

Задание для самостоятельной работы:

Разработать функцию и написать ее программный код, чтобы определять, принадлежит ли точка с координатами (х, у) окружности с заданным радиусом R. Данная функция должна быть с переменным числом параметров, т.е. проверять по несколько точек. Написать вызывающую функцию main, которая обращается к данной функции не менее двух раз с количеством параметров 4, 6 + дополнительные переменные при желании.

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

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

// Объявление двух перегруженных функций int func(int m, int n) {

if (m > n) { return m;

}

else return n;

}

double func(double m, double n) { if (m > n) {

return m;

}

else return n;

}

88

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

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

Функции не могут быть перегружены, если описание их параметров отличается только модификатором const или наличием ссылки.

Например, функции int& f1 (int&, const int&){…} и int f1(int, int){…}

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

ифункции, которая передает параметр по ссылке.

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

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

template <class имя_типа> // Заголовок шаблона заголовок функции {

тело функции;

}

Таким образом, шаблон семейства функций состоит из двух частей – заголовка шаблона:

template <список параметров шаблона>

и обыкновенного определения функции, в котором вместо типа возвращаемого значения и типа параметров записывается имя типа, определенное в заголовке шаблона [7].

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

template <class Tip> // Появляется новый тип

/*

При передаче параметров этот тип приобретает значение

89

(например, int)

*/

Tip func(Tip m, Tip n) {

// Эта функция возвращает значение нового типа if (m > n) {

return m;

}

else return n;

}

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

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

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

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

Задания для самостоятельной работы:

Разработать и написать в коде перегруженные функции

иосновную программу, которая их вызывает:

для массива целых чисел находит минимальный элемент;

для строки находит длину самого короткого слова.

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

10.5. Указатели на функции

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

int f1(char c){…} //определение функции int(*ptrf1)(char); //определение указателя на функцию f1 ptrf1=f1;//указателю на функцию присвоили адрес функции f1

90

Соседние файлы в папке книги