Класс, в котором определена операция вызова функции, т.е. операция "круглые скобки", называется функциональным. От такого класса не требуется наличия других полей и методов.
Операцию () можно определять только как метод класса. В классе можно определить перегруженные операции вызова функции (например, с различным числом аргументов). Функциональные объекты широко применяются в стандартной библиотеке шаблонов С++ (STL).
Рассмотрим пример 1 программы с таким классом. Функциональный класс реализует вычисление остатка от целочисленного деления:
//Листинг 17.1
#include <iostream>
using namespace std;
class Mod
{
public:
int operator () (int a, int b)
{
return a % b;
}
};
int main()
{
Mod x;
cout << 7 % 5 << endl; // результат 2
cout << x(7,5) << endl; // результат 2
cout << x.operator()(7,5); // результат 2
cout<< endl;
cout << Mod()(5,4) << endl; // результат 1
return 0;
}
Использование такого класса имеет весьма специфический синтаксис. Поскольку в классе Mod определена операция вызова функции с параметрами, выражение x(7,5) является допустимым (то же самое можно записать в виде x.operator()(7,5)). Как видно из примера, объект функционального класса используется так, как если бы он был функцией.
|
|
В пятом операторе вывода выражение Mod() используется для вызова конструктора по умолчанию класса Mod. Результатом выполнения этого выражения является анонимный объект класса Mod. Далее (как и в случае с x) для этого объекта вызывается функция с двумя аргументами, записанными в круглых скобках.
Объект функционального класса называется функтором или объектом-функцией или функциональным объектом.
Функтор, возвращающий булевский результат, называется функтором-предикатом. Функтор с одним аргументом называется унарным функтором, функтор с двумя аргументами – бинарным.
Функциональный класс является обычным классом, поэтому в нём могут быть определены любые поля, конструкторы, методы. Можно определить перегруженные операции вызова функции. Любые методы могут быть объявлены виртуальными. Функциональный класс может участвовать в иерархии наследования, может запрашивать динамическую память, может быть абстрактным.
Основное отличие функционального объекта от указателя на функцию – это возможность задавать для функционального объекта некоторое состояние. Класс функциональных объектов позволяет создавать объекты-функции с разными состояниями. При обращении к функциональным объектам с помощью операции "круглые скобки" возвращаемое значение (результат) зависит не только от значений аргументов, но и от внутреннего состояния функционального объекта.
Функторы обладают ещё одной важной особенностью по сравнению с указателями на функции. Тип обычной функции (и указателя на функцию) определяется только прототипом. Если прототипы одинаковы, то и тип один и тот же. Объекты функций могут быть разного типа даже при идентичных прототипах операции operator() – просто нужно назвать классы по-разному. Это открывает возможности унифицированного программирования с применением шаблонов, так как позволяет передавать поведение как параметр шаблона.
|
|
Рассмотрим пример 2 программы с функциональным классом, содержащим поля, конструктор и перегруженные операции вызова функции – унарный и бинарный предикаты.
// Листинг 17.2
#include <iostream>
using namespace std;
class Funct
{
int val;
public:
// конструктор с умолчанием
Funct(int v = 4): val(v)
{ }
// унарный предикат
bool operator () (int a)
{
return a == val;
}
// бинарный предикат
bool operator()(int a, int b)
{
return a > b;
}
};
int main()
{
Funct ob;
// вызывается бинарный предикат
cout << ob(1,5); // результат 0, т.к. 1<5
cout<< endl;
// вызывается унарный предикат
cout << ob(4); //результат 1, т.к. 4==4
cout<< endl;
return 0;
}
Рассмотрим пример 3 программы, в которой осуществляется нахождение максимального значения среди элементов одномерного массива согласно критерию: максимальным является элемент xi, для которого наибольшее значение имеет выражение a*xi*xi+b*xi, где a и b – некоторые коэффициенты. Нахождение максимального элемента оформлено в виде шаблона функции, которому в качестве одного из параметров передается функтор-предикат. В функциональном классе Pred определён бинарный предикат, задающий критерий вычисления максимума, а коэффициенты a и b заданы в виде полей класса.
// Листинг 17.3
#include <iostream>
using namespace std;
class Pred {
int a;
int b;
public:
// конструктор с умолчанием
Pred(int v1 = 1,int v2 = 0): a(v1), b(v2)
{ }
//бинарный предикат - задаём критерий //вычисления максимума:
//максимум величины a*x*x+b*x
bool operator()(int x, int y)
{
return a*x*x+b*x > a*y*y+b*y;
}
};
// шаблон функции нахождения максимального //элемента массива
// (или фрагмента массива от begin до end)
// согласно критерию,
//заданному функтором-предикатом
template <typename T, typename F>
T max(T* begin, T* end, F functor)
{
T *p, m;
p = begin;
m = *p;
for (p = begin+1; p!= end; ++p)
if (functor(*p,m)) m = *p;
return m;
}
int main()
{
const int n = 5;
int mas[n] = {-1, 3, 0, -5, 4};
// максимальный элемент,
//у которого самый большой квадрат значения,
//т.к. по умолчанию a = 1, b = 0
cout << max(mas,mas+n,Pred());//результат: -5
cout<< endl;
//максимальный элемент,
//у которого наибольшее значение,т.к. a=0, b=1
cout<<max(mas,mas+n,Pred(0,1));//результат: 4
cout<< endl;
return 0;
}
Задание:
Реализовать алгоритм сортировки одномерного массива, используя заданный критерий сравнения элементов. Сортировку массива оформить в виде шаблона функции, которой в качестве параметров передаются два указателя (первый идентифицирует начало сортируемого диапазона, а второй указывает на ячейку памяти, следующую за последним элементом из диапазона) и функтор-предикат. Создать класс согласно своему варианту и функциональный класс с перегруженными бинарными предикатами, задающими критерии сравнения элементов массива.
Критерий сравнения для элементов массива типа int: сравниваются квадраты значений элементов массива.
Критерий сравнения для элементов массива типа double: сравниваются абсолютные значения элементов массива.
Критерий сравнения для элементов массива объектов созданного класса указан в каждом варианте задания.
В функции main():
1) создать динамический массив из целых чисел (число элементов не менее 50); заполнить, используя генератор случайных чисел; отсортировать первые тридцать элементов массива;
2) создать динамический массив из вещественных чисел (число элементов не менее 25); заполнить, считывая информацию из заранее созданного неотсортированного файла; отсортировать с пятого по двадцатый элементы массива;
|
|
3) создать динамический массив объектов созданного класса (число элементов не менее 15); заполнить, считывая информацию из заранее созданного неотсортированного файла; отсортировать весь массив.
Вариант 1
а) сортировка вставками (по возрастанию);
б) класс прямоугольный треугольник (элементы-данные: два катета);
в) сравниваются площади треугольников.
Вариант 2
а) сортировка выбором (по возрастанию);
б) класс треугольник (элементы-данные: три стороны);
в) сравниваются периметры треугольников.
Вариант 3
а) сортировка обменом (по возрастанию);
б) класс прямоугольник (элементы-данные: длина и ширина);
в) сравниваются площади прямоугольников.
Вариант 4
а) сортировка вставками (по убыванию);
б) класс окружность (элементы-данные: координаты центра, радиус);
в) сравниваются длины окружностей.
Вариант 5
а) сортировка выбором (по убыванию);
б) класс прямоугольный параллелепипед (элементы-данные: длины трёх рёбер, имеющих общую вершину);
в) сравниваются объёмы параллелепипедов.
Вариант 6
а) сортировка обменом (по убыванию);
б) класс прямой цилиндр (элементы-данные: радиус цилиндра и высота);
в) сравниваются площади боковых поверхностей цилиндров.
Вариант 7
а) сортировка вставками (по возрастанию);
б) класс шар (элементы-данные: радиус);
в) сравниваются объёмы шаров.
Вариант 8
а) сортировка выбором (по возрастанию);
б) класс правильная пирамида, основанием которой является квадрат (элементы-данные: сторона квадрата, высота пирамиды);
в) сравниваются объёмы пирамид.
Вариант 9
а) сортировка обменом (по возрастанию);
б) класс прямая призма, основанием которой является треугольник (элементы-данные: стороны треугольника, высота призмы);
в) сравниваются площади боковых поверхностей призм.
Вариант 10
а) сортировка вставками (по убыванию);
б) класс конус (элементы-данные: радиус основания, высота конуса);
в) сравниваются полные площади поверхностей конусов.
|
|
Вариант 11
а) сортировка выбором (по убыванию);
б) класс ромб (элементы-данные: две диагонали);
в) сравниваются площади ромбов.
Вариант 12
а) сортировка обменом (по убыванию);
б) класс правильная пирамида, основанием которой является треугольник (элементы-данные: сторона треугольника, высота пирамиды);
в) сравниваются полные площади поверхностей пирамид.
Вариант 13
а) сортировка вставками (по возрастанию);
б) класс кольцо на плоскости (элементы-данные: внутренний и внешний радиусы);
в) сравниваются площади колец.
Вариант 14
а) сортировка выбором (по возрастанию);
б) класс тор (элементы-данные: внутренний и внешний радиусы, толщина тора);
в) сравниваются объёмы торов.
Вариант 15
а) сортировка обменом (по возрастанию);
б) класс усечённый конус (элементы-данные: радиусы нижнего и верхнего оснований, высота конуса);
в) сравниваются объёмы усечённых конусов.
Вариант 16
а) сортировка вставками (по убыванию);
б) класс рациональная дробь (элементы-данные: целочисленные числитель и знаменатель);
в) сравниваются суммы квадратов числителя и знаменателя дробей.
Вариант 17
а) сортировка выбором (по убыванию);
б) класс книга (элементы-данные: автор, название, год выпуска);
в) сравниваются лексикографически значения сцеплённых строк (автор+название).
Вариант 18
а) сортировка обменом (по убыванию);
б) класс телефон (элементы-данные: номер телефона, фамилия владельца, стаж пользования в годах, сумма денег на счёте);
в) сравниваются величины: сумма денег на счёте + бонус (5% от суммы, умноженные на стаж).
Вариант 19
а) сортировка вставками (по возрастанию);
б) класс комплексное число (элементы-данные: вещественная и мнимая части);
в) сравниваются модули комплексных чисел.