§14 Функции возвращающие значение. Перегрузка функций. Шаблоны. Раздельная компиляция

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

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

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

Объявление функции. Прототип

Тип_возвращаемого_значения Имя_функции(список типов_параметров);

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

int main() {
    // Операция вызова в выражении с присваиванием:
    T Var = Имя_функции(список аргументов);
    // Операция вызова в потоке вывода:
    cout << Имя_функции(список аргументов);
}

Определение функции

Тип_возвращаемого_значения Имя_функции(список параметров) {
    // Инструкции
    return Выражение_возвращающее значение;
}

Инструкция return

Инструкция return завершает выполнение функции и возвращает её значение. Инструкция return возвращает только одно значение (переменной или выражения). В некоторых случаях оператор return может применяться и в функциях не возвращающих значение (cpp-13.2). Но в этих функциях пустая инструкция return применяется для выхода из функции и возврата управления в вызывающую программу.
Приведем пример программы с функцией возвращающей значение.
Составим программу с функцией возвращающей случайное значение на отрезке [a, b]. Назовем ее myRand(). Эта функция двух аргументов может применяться, например, для заполнения массива.

Программа cpp-14.1
#include <iostream>
#include <ctime>
#include <random>
using namespace std;

int myRand(int, int);

int main() {
	int a, b, r, n;
	// Границы отрезка:
	cout << "a = "; cin >> a;
	cout << "b = "; cin >> b;
	// Сколько случайных чисел нужно выводить?
	cout << "n = "; cin >> n;
	for (int i = 1; i <= n; i++) {
		r = myRand(a, b);
		cout << r << endl;
	}
	return 0;
}

int myRand(int x, int y) {
    static default_random_engine rnd(time(0));
    static uniform_int_distribution<unsigned> d(x, y);
	return d(rnd);
}

Вывод

a = 7
b = 23
n = 4
7
13
12
20

В теле функции может быть несколько инструкций return. В таком случае только одна инструкция return вернет значение. Это можно запрограммировать с помощью условной инструкции. Приведем пример такой программы. Дано двузначное целое число n. Если это число четное получить произведение его цифр, иначе получить сумму его цифр.

Программа cpp-14.2
#include <iostream>
using namespace std;

int resPow(int);

int main() {
	int n;
	cout << "n = "; cin >> n;
	cout << resPow(n) << endl;
	return 0;
}

int resPow(int m) {
	if (m % 2 == 0)
		return (m % 10) * (m / 10);
	else
		return m % 10 + m / 10;
}

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

Программа cpp-14.3.1
#include <iostream>
using namespace std;

pair<int, int> division(int, int);

int main() {
    int a, b;
    cout << "Введите 1 число: "; cin >> a;
    cout << "Введите 2 число: "; cin >> b;

    cout << "Целое: "     << division(a, b).first
         << "\nОстаток: " << division(a, b).second
         << endl;
    return 0;
}

pair<int, int> division(int x, int y){
    return {x / y, x % y};
}

Вывод

Введите 1 число: 9
Введите 2 число: 2
Целое: 4
Остаток: 1

Решение этой задачи можно оформить и по другому. Такой способ называется декомпозиция. Декомпозиция позволяет получить связь данных, находящихся в структуре, с переменными, используя спецификатор auto и [] следующим образом:

Программа cpp-14.3.2
int main() {
    int a, b;
    cout << "Введите 1 число: "; cin >> a;
    cout << "Введите 2 число: "; cin >> b;
    auto [who, rem] = division(a, b);
    cout << "Целое: "     << who
         << "\nОстаток: " << rem
         << endl;
    return 0;
}

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

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

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

Программа cpp-14.4
#include <iostream>
using namespace std;

double avr(double, double);
double avr(double, double, double);

int main() {
    double x, y, z;
    cin >> x >> y >> z;
    cout << avr(x, y)    << endl
         << avr(x, y, z) << endl;
	return 0;
}

double avr(double p, double n) {
    return p * n / 2;
}

double avr(double p, double n, double m) {
	return (p + n + m) / 3;
}

Вывод

1.2 5.3 7.8
3.18
4.76667

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

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

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

template <typename T1, typename T2, ...>
Определение функции

где T1, T2 - произвольные имена параметров типа шаблона. Каждому параметру типа должно предшествовать ключевое слово typename. Эти параметры применяются для указания типов различных объектов в заголовке и в теле функции. Компилятор свяжет эти параметры с соответствующим типом аргументов для определения типа параметров функции.
Приведем пример задачи. Составить программу в которой необходимо сравнивать два объекта одного типа. Сами же объекты могут быть разного типа.

Программа cpp-14.5
#include <iostream>
using namespace std;

template <typename T>
bool comp(T, T);

int main() {
    int x1 = 10, x2 = 5;
    char c1 = 'a', c2 = 'z';
    string s1("sun"), s2("moon");
    cout << boolalpha << comp(x1, x2) << endl
         << boolalpha << comp(c1, c2) << endl
         << boolalpha << comp(s1, s2) << endl;
    return 0;
}

template <typename T>
bool comp(T a, T b) {
    return (a > b);
}

Вывод

true
false
true

Шаблоны в C++ являются основой для обобщенного программирования, целью которого является возможность написания алгоритмов очень высокого уровня абстракции. Шаблоны широко применяются в стандартной библиотеке C++. Большой вклад в появление развитых средств обобщённого программирования в C++ внёс Александр Степанов - русско-американский учёный в области информатики и вычислительной техники. В 1995 г. А. Степанов получил премию Dr.Dobb’s Excellence In Programming Award за создание STL, которую разделил с Линусом Торвальдсом (создателем Linux).

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

При проектировании приложения большую программу на 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) перечислим прототипы функций, которые мы будем использовать в программе:

Программа cpp-14.4.1
#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):

Программа cpp-14.4.2
#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. Реализация программного кода в этом файле могла бы выглядеть следующим образом:

Программа cpp-14.4.3
#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;
}
Примеры решения задач
  • 1. Составить программу в которой используется функция нахождения максимального из двух чисел.
  • #include <iostream>
    using namespace std;
    
    int maxAorB(int, int);
    
    int main() {
        int a, b;
        cout << "a = "; cin >> a;
        cout << "b = "; cin >> b;
        cout << "Максимальное из двух чисел "
             << maxAorB(a, b)
             << endl;
    
        return 0;
    }
    int maxAorB(int x, int y) {
        return (x > y) ? x : y;
    }
    
    
  • 2. Составить программу в которой бы использовалась логическая функции, определяющая четное ли введенное число k.
  • #include <iostream>
    using namespace std;
    
    bool even(int);
    
    int main() {
        int k;
        cout << "k = "; cin >> k;
        cout << k << (even(k) ? " чётное" : " нечётное");
    
        //if (even(k))
        //    cout << k << " чётное" << endl;
        //else
        //    cout << k << " нечётное" << endl;
    
        return 0;
    }
    bool even(int x) {
        return !(x % 2) ? true : false;
    }
    

    Примечание. В комментариях – вариант с условной инструкцией if.

  • 3. Составить программу нахождения наибольшего общего делителя (НОД) двух чисел, используя алгоритм Евклида (НОД(a - b, a) = НОД(a, b)), с помощью функции.
  • #include <iostream>
    using namespace std;
    
    int nod(int, int);
    
    int main() {
        int a, b;
        cout << "a = "; cin >> a;
        cout << "b = "; cin >> b;
        cout << "НОД (" << a << ", " << b << ") = " << nod(a, b) << endl;
    
        return 0;
    }
    int nod(int x, int y) {
        while(x != y) {
            if (x > y)
                x = x - y;
            else
                y = y - x;
        }
        return x;
    }
    

    Вариант функции с использованием операции % – взятия остатка от деления:

    int nod(int x, int y) {
        while(x && y) {
            if (x >= y)
                x %= y;
            else
                y %= x;
        }
        return x | y; // побитовое or
    }
    
  • 5. Составить программу в которой используется функция для вычисления F(x):
  • funct1

    #include <iostream>
    using namespace std;
    
    float f(float);
    
    int main() {
    	float x;
    	cout << "x = "; cin >> x;
    	cout << "f(x) = " << f(x) << endl;
    	return 0;
    }
    
    float(float g) {
    	if (g <= 3)
    		return g * g - 3 * g + 9;
    	else
    		return 1 / (g * g * g + 6);
    }
    

Вопросы
  1. В чем различие функции возвращающей и не возвращающей значение?
  2. Когда перегрузка функций будет полезна, а в каких случаях её нужно избегать?
  3. Когда применяется шаблон функции?
  4. В каких случаях в одной функции применяется две инструкции return?
  5. В каких случаях функцию можно использовать как “логическую”?
Темы сообщений
Задания А
  1. Составьте функцию, определяющую сумму цифр введенного целого числа.
  2. Напишите программу в которой используется перегруженная функция разворачивающая числа и слова задом-наперед. Например, 7890 -> 0987, qwerty -> ytrewq.
Задания Б
Задания С
Ссылки
1 Звезда2 Звезды3 Звезды4 Звезды5 Звезд (Пока оценок нет)
Загрузка...

Если вы нашли ошибку, пожалуйста, выделите фрагмент текста и нажмите Ctrl+Enter.


Обсуждение закрыто.