一起来学习C++的函数指针和函数对象

一起来学习C++的函数指针和函数对象

目录

函数指针

函数对象

总结

函数指针

以下是<cstdlib>库中的一个排序数组的方法qsort()的函数原型。

void qsort (void* base, size_t num, size_t size, int (*compar)(const void*, const void*));

base -- 指向要排序的数组的第一个元素的指针。

num -- 由 base 指向的数组中元素的个数。

size -- 数组中每个元素的大小,以字节为单位。

compar -- 用来比较两个元素的函数。

对于可以使用常规关系运算符进行比较的类型,常规比较函数可能如下所示:

int compareMyType (const void * a, const void * b) { if ( *(MyType*)a < *(MyType*)b ) return -1; if ( *(MyType*)a == *(MyType*)b ) return 0; if ( *(MyType*)a > *(MyType*)b ) return 1; } #include <cstdlib> #include <iostream> int cmpfunc (const void* a, const void* b); using namespace std; int main() { int values[] = { 88, 56, 100, 2, 25 }; qsort(values, sizeof(values)/sizeof(int), sizeof(int), cmpfunc); cout << "排序之后的列表:" << endl; for(int n = 0 ; n < 5; n++ ) { cout << values[n] << " "; } return 0; } int cmpfunc (const void* a, const void* b) { return ( *(int*)a - *(int*)b ); } Enter a string (empty line to quit):|abc<Enter> Enter menu choice: u) uppercase l) lowercase t) transposed case o) original case n) next string Please enter u, l, t, o, or n: |u<Enter> ABC Enter menu choice: u) uppercase l) lowercase t) transposed case o) original case n) next string Please enter u, l, t, o, or n: |l<Enter> abc #include <cstdio> #include <cstring> #include <string> #include <cctype> #include <iostream> #define LEN 81 char showmenu(); void show(void (* fp)(char*), char* str); void ToUpper(char*); // 把字符串转换为大写 void ToLower(char*); // 把字符串转换为小写 void Transpose(char*); // 大小写转置 void Dummy(char*); // 不更改字符串 using namespace std; int main() { char line[LEN]; char copy[LEN]; char choice; void (* pfun)(char*); // 声明一个函数指针, 被指向的函数接受char *类型的参数, 无返回值 cout << "Enter a string (empty line to quit):"; while (cin >> line) { while ((choice = showmenu()) != 'n') { switch (choice) { // switch语句设置指针 case 'u': pfun = ToUpper; break; case 'l': pfun = ToLower; break; case 't': pfun = Transpose; break; case 'o': pfun = Dummy; break; } strcpy(copy, line); // 为show()函数拷贝一份 show(pfun, copy); // 根据用户的选择, 使用选定的函数 } cout << "Enter a string (empty line to quit):"; } cout << "Bye!"; return 0; } char showmenu() { char ans; cout << "Enter menu choice:" << endl; cout << "u) uppercase l) lowercase" << endl; cout << "t) transposed case o) original case" << endl; cout << "n) next string" << endl; ans = getchar(); // 获取用户的输入 ans = tolower(ans); // 转换为小写 while (strchr("ulton", ans) == NULL) { cout << "Please enter u, l, t, o, or n:" << endl; ans = tolower(getchar()); } return ans; } void show(void (* fp)(char*), char* str) { (*fp)(str); // 把用户选定的函数作用于str cout << str << endl; // 显示结果 } void ToUpper(char* str) { while (*str) { *str = toupper(*str); str++; } } void ToLower(char* str) { while (*str) { *str = tolower(*str); str++; } } void Transpose(char* str) { while (*str) { if (islower(*str)) *str = toupper(*str); else if (isupper(*str)) *str = tolower(*str); str++; } } void Dummy(char* str) { } //不改变字符串 函数对象

函数对象是专门设计用于语法与函数相似的对象。在C++中,这是通过在类中定义成员函数operator()来实现的,例如:

struct myclass { int operator()(int a) { return a; } } myobject; int x = myobject(0);

它们通常用作函数的参数,例如传递给标准算法的谓词或比较函数。

标准库预先定义了些function object。所谓function object,是某种class的实例对象,这类class对function call运算符做了重载操作,如此一来可使function object被当成一般函数来使用。

function object实现了我们原本可能以独立函数加以定义的事物。但又何必如此呢?
主要是为了效率。我们可以令call运算符成为inline,从而消除“通过函数指针来调用函数”时需要付出的额外代价。

标准库事先定义了一组function object,分为:

算术运算(arithmetic)、关系运算(relational)和逻辑运算(logical)三大类。

以下列表中的type在实际使用时会替换为内置类型或class类型:

6个算术运算

plus<type>,minus<type>,negate<type>,

multiplies<type>,divides<type>,modules<type>

6个关系运算

less<type>,less_equal<type>,greater<type>,

greater_equal<type>,equal_to<type>,not_equal_to<type>

3个逻辑运算logical_and<type>,logical_or<type>,logic_not<type>

要使用事先定义的function object,首先得包含相关头文件:<functional>

默认情况下sort()是升序排列,我们将元素降序排列:

sort(vec.begin(), vec.end(), greater<int>());

其中的greater<int>()会产生一个未命名的class template object,传给sort()。

binary_search()期望其搜索对象先经过排序,为了正确搜索vector,就必须传给它某个function object object,供vector排序使用:

binary_search(vec.begin(), vec.end(), elem, greater<int>());

我们对Fibonacci数列可以做些其他操作,如:每个元素和自身相加、和自身相乘、被加到对应的Pell数列等等。做法之一是使用泛型算法transform()并搭配plus<int>和multiplies<int>。

我们必须传给transform()的参数有:

➀一对iterator,标示出欲转换的元素范围;

➁一个iterator,所指元素将应用于转换上,元素范围同➀;

➂一个iterator,所指位置(及其后面的空间)用来存放转换结果;

➃一个function object,表现出我们想要应用的转换操作。

以下是将Pell数列加到Fibonacci数列的写法:

transform(fib.begin(), fib.end(), //➀ pell.begin(), //➁ fib_plus_pell.begin(), //➂ plus<int>); //➃

transform()的定义:

function template <algorithm> std::transform

unary operation(1) template <class InputIterator, class OutputIterator, class UnaryOperation> OutputIterator transform(InputIterator first1, InputIterator last1, OutputIterator result, UnaryOperation op); binary operation(2) template <class InputIterator1, class InputIterator2, class OutputIterator, class BinaryOperation> OutputIterator transform(InputIterator1 first1, InputIterator1 last1, InputIterator2 first2, OutputIterator result, BinaryOperation binary_op); ———————————————————————————————————————————————————— 将操作顺序应用于一(1)或两(2)个范围的元素,并将结果存储在从结果开始的范围中。 (1) 一元操作 将op应用于[first1,last1]范围内的每个元素,并将每个操作返回的值存储在从result开始的范围内。 (2) 二元操作 使用范围[first1,last1]中的每个元素作为第一个参数,并使用范围中从first2开始的各个参数作为 第二个参数来调用binary_op。每个调用返回的值存储在从result开始的范围中。 该函数允许目标范围与其中一个输入范围相同,以便进行适当的转换。

函数对象适配器:

function object less<type>期望外界传入两个值,如果第一个值小于第二个值就返回true。本例中,每个元素都必须和用户所指定的数值进行比较。理想情形下,我们需要将less<type>转化为一个一元(unary)运算符。这可通过“将其第二个参数绑定(bind)至用户指定的数值”完成。这么一来less<type>便会将每个元素拿出来一一与用户指定的数值比较。

真的可以做到这样吗?是的。标准库提供adapter(适配器)便应此而生。

function object adapter会对function object进行修改操作。binder adapter(绑定适配器)会将function object的参数绑定至某特定值,使binary(二元) function object转化为unary(一元)function object。这正是我们需要的。

标准库提供了两个binder adapter

bind1st会将指定值绑定至第一操作数;

bind2nd将指定值绑定至第二操作数。

如:a < b,则a是第一操作数,b是第二操作数。

vector<int> filter<const vector<int> &vec, int val, less<int> &lt) { vector<int> nvec; vector<int>::const_iterator iter = vec.begin(); while ((iter = find_if(iter, vec.end(), bind2nd(lt, val))) != vec.end()) { nvec.push_back(*iter); iter++; } return nvec; }

bind2nd(less, val);会把val绑定于less<int>的第二个参数身上。于是,less<int>会将每个元素拿来和val比较。上例第一操作数是*iter,第二操作数就是固定值val。如果*iter<val则true。

find_if()的定义如下:

template <class InputIterator, class UnaryPredicate> InputIterator find_if(InputIterator first, InputIterator last, UnaryPredicate pred); ●first、last:输入迭代器到序列的初始和最终位置。使用的范围是[first,last),它包含first和last之间的所有元素,包括first指向的元素,但不包括last指向的元素。 ●pred:接受范围内的元素作为参数并返回可转换为bool类型的值的【一元函数】。返回的值表明该元素是否被认为是此函数的上下文中的匹配。 函数不能修改它的参数。 它可以是函数指针,也可以是函数对象(function object)。 ●返回值:指向pred不返回false的范围内第一个元素的迭代器。 如果pred对所有元素都为false,则函数返回last。 这个函数模板的行为相当于: template<class InputIterator, class UnaryPredicate> InputIterator find_if(InputIterator first, InputIterator last, UnaryPredicate pred) { while (first!=last) { if (pred(*first)) return first; ++first; } return last; }

下面看一个泛型函数find_if()的例子:

#include <iostream> // std::cout #include <algorithm> // std::find_if #include <vector> // std::vector bool IsOdd (int i) { return ((i%2)==1); } int main () { std::vector<int> myvector; myvector.push_back(10); myvector.push_back(25); myvector.push_back(40); myvector.push_back(55); std::vector<int>::iterator it = std::find_if(myvector.begin(), myvector.end(), IsOdd); std::cout << "The first odd value is " << *it << '\n'; return 0; } The first odd value is 25

看一个bind2nd()和bind1st()的例子:

#include <iostream> #include <functional> #include <algorithm> using namespace std; int main () { int numbers[] = {10,-20,-30,40,-50}; int cx = count_if(numbers, numbers+5, bind2nd(less<int>(), 0)); cout << "There are " << cx << " negative elements.\n"; return 0; } There are 3 negative elements. #include <iostream> #include <functional> #include <algorithm> using namespace std; int main () { int numbers[] = {10,-20,-30,40,-50}; int cx = count_if(numbers, numbers+5, bind1st(less<int>(), 0)); cout << "There are " << cx << " positive elements.\n"; return 0; } There are 2 positive elements. 总结

本篇文章就到这里了,希望能够给你带来帮助,也希望您能够多多关注易知道(ezd.cc)的更多内容!   

推荐阅读

    excel怎么用乘法函数

    excel怎么用乘法函数,乘法,函数,哪个,excel乘法函数怎么用?1、首先用鼠标选中要计算的单元格。2、然后选中单元格后点击左上方工具栏的fx公

    excel中乘法函数是什么?

    excel中乘法函数是什么?,乘法,函数,什么,打开表格,在C1单元格中输入“=A1*B1”乘法公式。以此类推到多个单元。1、A1*B1=C1的Excel乘法公式

    标准差excel用什么函数?

    标准差excel用什么函数?,函数,标准,什么,在数据单元格的下方输入l标准差公式函数公式“=STDEVPA(C2:C6)”。按下回车,求出标准公差值。详细

    金蝶凭证排序号乱了

    金蝶凭证排序号乱了,,1.金蝶的顺序号跟凭证号不一致怎么办没关系的,可以在凭证过滤界面选择按凭证号或者凭证顺序号来排序,一般都选择凭证号

    excel常用函数都有哪些?

    excel常用函数都有哪些?,函数,哪些,常用,1、SUM函数:SUM函数的作用是求和。函数公式为=sum()例如:统计一个单元格区域:=sum(A1:A10)  统计多个

    word图标排序快捷键|word的快捷图标

    word图标排序快捷键|word的快捷图标,,1. word的快捷图标1、大家说的都是如何打开word,而不是像建空白文件夹那样,因为没有直接新建空白word

    在excel中如何排序

    在excel中如何排序,排序,如何,excel,先选定工作表要排序的数据范围,然后点击上方的“数据”选项,选中“排序”,出现如下画面选择好“主要