• 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++ 仿函数

  • 相关阅读:
    micro-app-源码解析4-数据通信篇-终篇
    【Kafka】Kafka的重复消费和消息丢失问题
    Java并发(二十)----synchronized原理进阶
    Sampling Area Lights
    Html第5集:DOM 事件 、JavaScript 事件
    UE4 回合游戏项目 10- 添加怪物死亡动画
    计算机毕业设计JavaNBA篮球资讯网(源码+系统+mysql数据库+lw文档)
    C++ 函数传递数组
    面试准备-中文面试问答(非技术)
    PyTorch深度学习实践1——线性回归和Logistic回归
  • 原文地址:https://blog.csdn.net/u013213111/article/details/126696042