• C++仿函数真好用


    仿函数(functor),就是使一个类的使用看上去像一个函数。其实现就是类中实现一个operator(),这个类就有了类似函数的行为。

    简单举个例子,调用printFunctor pf("!"); pf("Hello world");,输出为Hello world!

    class printFunctor
    {
    public:
    	printFunctor(const string &s) : ss(s) {};
    
    	void operator()(const string &str) const {
    		cout << str << ss << endl;
    	}
    private:
    	string ss;
    };
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    用一个在数组中统计符合某些条件(比如大于一个数)的元素个数的例子来说明仿函数的作用吧。

    用函数指针来写可以是这样:

    bool isGreaterThan(double &num, double &thres)
    {
    	return (num > thres);
    }
    
    int countFun(double *array, const int &size, double th, bool (*fun)(double &, double &)){
    	int cnt = 0;
    	for(int i = 0; i < size; i++)
    		if(fun(array[i], th))
    			cnt++;
    	return cnt;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    这样做有2个问题:
    1.数组元素类型是固定的(此处是double)。
    很自然地可以想到,如果用函数模板是不是就可以支持其他类型呢?这样的话,countFun函数的第4个参数就需要传入一个“指向函数模板的函数指针”,并不可行。

    在C++中,模板函数仅仅是一个用来生成函数的代码块,它本身是没有实体的,也就没有与“未被实例化的那些代码”相对应的程序代码块,所以也就无法对其取地址(不存在的东西,怎么会有具体的内存地址呢?)。只有在用具体类型代替模板参数,对该模板进行实例化以后才能有函数实体。
    而函数指针要指向函数的入口地址,那么既然函数模板没有具体的内存地址,那么指向函数模板的函数指针如何得到地址呢?所以所谓的“函数模板指针”这个定义是无法通过以下的方法实现的:template void (*sample)(T &);
    ——https://www.cnblogs.com/superpig0501/p/3967576.html

    2.作为countFun函数参数的函数指针bool (*fun)(double &, double &),形式已被确定,很难灵活应变(例如再传入2个参数用于指定元素上下限范围)。

    下面来看看如何用仿函数解决上述问题。

    1.用仿函数来支持多种数组元素类型:

    template<typename T>
    class operationFunctor
    {
    public:
    	virtual bool operator()(const T &num) = 0; //Pure virtual function
    };
    
    template<typename T>
    class gtFunctor : public operationFunctor<T>
    {
    public:
    	gtFunctor(const T &_th) : thres(_th) {};
    	bool operator()(const T &num) {
    		return (num > thres);
    	}
    private:
    	T thres;
    };
    
    template<typename T>
    int countFun(T *arr, int size, operationFunctor<T> &op)
    {
    	int cnt = 0;
    	for(int i = 0; i < size; i++)
    		if(op(arr[i]))
    			cnt++;
    	return cnt;
    }
    
    • 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

    countFun函数的第3个参数也可以写成类指针:

    template<typename T>
    int countFun(T *arr, int size, operationFunctor<T> *op)
    {
    	int cnt = 0;
    	for(int i = 0; i < size; i++)
    		if((*op)(arr[i])) //ATTENTION:(*op)(arr[i]), not *op(arr[i])!
    			cnt++;
    	return cnt;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    2.用仿函数支持灵活地传参

    template<typename T>
    class gtLimFunctor : public operationFunctor<T>
    {
    public:
    	gtLimFunctor(const T &_max, const T &_min, const T &_th) : max(_max), min(_min), thres(_th) {};
    	bool operator()(const T &num) {
    			return (num > min && num < max && num > thres);
    	}
    private:
    	T max;
    	T min;
    	T thres;
    };
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    最后附上主函数吧:

    int main()
    {
    	printFunctor pf("!");
    	pf("Hello world");
    
    	const int size = 10;
    
    	double doubleArr[size];
    	for(int i = 0; i < size; i++)
    		doubleArr[i] = i * 0.1;
    
    	int intArr[size];
    	for(int i = 0; i < size; i++)
    		intArr[i] = i;
    
    	int cnt1 = countFun(doubleArr, size, 0.6, isGreaterThan);
    	cout << "Count1 " << cnt1 << endl;
    
    	gtFunctor<double> doubleGt(0.4);
    	int cnt2 = countFun<double>(doubleArr, size, doubleGt);
    	cout << "Count2 " << cnt2 << endl;
    
    	gtFunctor<int> intGt(7);
    	int cnt3 = countFun<int>(intArr, size, intGt);
    	cout << "Count3 " << cnt3 << endl;
    
    	int cnt4 = countFun<double>(doubleArr, size, new gtFunctor<double>(0.8));
    	cout << "Count4 " << cnt4 << endl;
    
    	int cnt5 = countFun<int>(intArr, size, new gtLimFunctor<int>(6, 1, 3));
    	cout << "Count5 " << cnt5 << endl;
    
    	return 0;
    }
    
    • 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

    参考:
    C++ 仿函数

  • 相关阅读:
    【代码随想录】栈与队列专栏(java版本)
    k8s-list-watch集群资源调度
    electron+vue3 实战
    CorelDRAW最新24.1.0.360版本更新介绍讲解
    【5. 事务】
    RSA加密和解密原理及过程(非对称加密)
    SpringMVC系列(五)之JSR303和拦截器
    【408考点之数据结构】线性表的顺序表示
    Vue2基础学习
    Java 序列化和反序列化为什么要实现 Serializable 接口呢?
  • 原文地址:https://blog.csdn.net/u013213111/article/details/126696042