Практическое занятие 17. Функторы

 

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

Операцию () можно определять только как метод класса. В классе можно определить перегруженные операции вызова функции (например, с различным числом аргументов). Функциональные объекты широко применяются в стандартной библиотеке шаблонов С++ (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

а) сортировка вставками (по возрастанию);

б) класс комплексное число (элементы-данные: вещественная и мнимая части);

в) сравниваются модули комплексных чисел.

 



Понравилась статья? Добавь ее в закладку (CTRL+D) и не забудь поделиться с друзьями:  



double arrow
Сейчас читают про: