У попередніх обговореннях вже неодноразово з'являвся такий термін як функтор, але особливої актуальності він набуває стосовно алгоритмам. Тепер настав час розібратися з цим поняттям. Функтор - це скорочення від еункціональний об'єкт, представляє собою конструкцію, що дозволяє використовувати об'єкт класу як функцію. У C ++ для визначення функтора досить описати клас, в якому перевизначена операція ().
те, як з об'єкта утворюється функція, легко показати на такому простому прикладі:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 | #include <iostream> #include <vector> using namespace std; class summator : private vector<int> { public: summator(const vector<int>& ini) { for (auto x : ini) this->push_back(x); } int operator()(bool even) { int sum = 0; auto i = begin(); if (even) i++; while (i < end()) { sum += *i++; if (i == end()) break; i++; } return sum; } }; int main(void) { setlocale(LC_ALL, "rus"); summator sums(vector<int>({ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 })); cout << "сумма чётных = " << sums(true) << endl << "сумма нечётных = " << sums(false) << endl; } |
Уже з такого простого прикладу видно наступне: операция () в класі може бути перевизначена (точніше визначена, оскільки вона не має реалізації за замовчуванням) з довільним числом, типом параметрів і типом значення, що повертається (або навіть зовсім без значення, що повертається). В підсумку:
Вигода функтора полягає в тому, що а). його можна параметризрвані при створенні об'єкта (перед викликом) використовуючи конструктор об'єкта з параметрами і б). може створюватися тимчасовий об'єкт виключно на час виконання функціонального виклику. Це ілюструється прикладом такого спрощеного целочисленного калькулятора:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 | #include <iostream> using namespace std; class calculate { char op; public: calculate( char op ) : op( op ) {} int operator()( int op1, int op2 ) { switch( op ) { case '+': return op1 + op2; case '-': return op1 - op2; case '*': return op1 * op2; case '/': return op1 / op2; case '%': return op1 % op2; case '^': { int ret = op1; while( op2-- > 1 ) ret *= op1; return ret; } default: cout << "неразрешённая операция" << endl; return 0; } } }; int main( int argc, char **argv, char **envp ) { setlocale(LC_ALL, "rus"); char oper; int op1, op2; do { cout << "выражение для вычисления (<op1><знак><op2>): " << flush; cin >> op1 >> oper >> op2; cout << op1 << ' ' << oper << ' ' << op2 << " = " << calculate( oper )( op1, op2 ) << endl; } while( true ); return 0; } |
Тут в рядку cout << обчислювати( опер )( op1, op2 ) дії виконуються послідовно:
створюється тимчасовий об'єкт класса обчислювати конструктором з параметром опер;
для цього об'єкта виконується метод () (функціональний виклик) з двома параметрами;
операция, яка буде виконана в цьому функціональному виклику, залежить від того параметра опер, з яким був сконструйований об'єкт;
функціональний виклик повертає значення результату операції;
створений тимчасовий об'єкт, відразу ж після цього руйнується (якби у нього був описаний деструктор, то він би викликався в цій точці);
І в результаті ми отримуємо:
Але особливо широке застосування функтори придбали в алгоритмах STL, розглянутих раніше, коли вони передаються в виклик в якості параметра, замість функції, визначальною дію або предикат алгоритму.
А чому в першому прикладі символ : стоїть перед словом public? Поясніть, будь ласка)
Тобто перед private)
Це вже з іншого розділу C ++: классы, області видимості.
У прикладі private відноситься до видимості базового класу: доступ до базового класу існує тільки зсередини класу summator.
Кваліфікатор public далі вже визначений всередині класса, і відноситься до функцій членам, оголошеним після нього: конструктору і оператору (). Він показує що ці функції члени видимі і можуть бути використані в будь-якому місці програми.
В даному прикладі все це не так важливо, але це окрема і важлива частина мови C ++.
t6d8rk