• C++ 函数调用运算符与可调用对象


    函数调用运算符

    函数调用运算符用“operator ()”表示。如果类重载了函数调用运算符,可以像使用函数一样使用该类对象。
    例如,像下面一样重载了某个struct或class的operator(),其对象就能像函数一样调用。

    // 类重载operator()
    struct absInt { // class一样可以
    	int operator()(int val) const { // 定义运算符operator(), 求val绝对值
    		return val < 0 ? -val : val; 
    	}
    	// ... 其他成员函数或者成员变量声明(如果有的话)
    };
    
    // “调用”该类对象
    absInt obj; // 函数对象
    int res = obj(-10); // 传递参数10给obj.operator(), res值为10
    

    [======]

    函数对象

    函数对象基本概念

    如果类定义了operator()(函数调用运算符),那么这样的类的对象称为函数对象(function object),其调用行为类似于调用函数。
    函数对象跟普通对象没有本质区别,也可以有自己的成员变量和成员函数。

    例如,定义class PrintString

    // 定义函数对象对应的类
    class PrintString {
    public:
    	PrintString(ostream& o = cout, char c = ' ') : os(o), seperator(c) { }
    	void operator()(const string &s) const { os << s << seperator; } // 定义operator()
    	~PrintString(){}
    private:
    	ostream &os; // 输出流
    	char seperator; // 不同输出分隔符
    };
    
    /* 使用函数对象 */
    string s = "test";
    PrintString printer; // 定义函数对象, 使用构造函数默认值, 打印到cout, 分隔符为' '
    printer(s); 
    PrintString errors(cerr, '\n'); // 定义函数对象, 打印到cerr, 分隔符'\n'
    errors(s);
    
    // 打印一个数组
    for_each(vec.begin(), vec.end(), PrintString(cerr, '\n'));
    

    [======]

    lambda与函数对象

    lambda本质是匿名函数对象,其产生的类中含有一个重载的函数调用运算符。
    例如,对vec数组进行排序。下面的lambda表达式[](const int a, const int b) { return a < b;} 其实是一个函数对象

    #include <algorithm>
    
    vector<int> vec;
    // ... // 往vec添加数据
    // lambda表达式
    stable_sort(vec.begin(), vec.end(), [](const int a, const int b) {
    	return a < b;
    });
    
    // <=> 等价于(函数指针)
    stable_sort(vec.begin(), vec.end(), compareFunc);
    
    bool compareFunc(int a, int b)
    {
    	return a < b;
    }
    
    // <=> 等价于(函数对象)
    struct Compare
    {
    	bool operator()(int a, int b)
    	{
    		return a < b;
    	}
    };
    
    Compare cobj;
    stable_sort(vec.begin(), vec.end(), cobj);
    

    [======]

    lambda值捕获与函数对象

    通过值捕获的局部变量,在lambda对应类中,相当于private数据成员。

    // 在words数组中, 找到第一个满足条件(lambda表示返回true)的元素的迭代器. 条件是对应元素的size() >= sz
    auto wc = find_if (words.begin(), words.end(), [sz](const string& a) {
    	return a.size() >= sz;
    });
    
    // 该lambda对应类
    class SizeComp {
    public:
    	SizeComp(size_t n) : sz(n) { }
    	bool operator()(const string& s) const {
    		return s.size() >= sz;
    	}
    private:
    	size_t sz;
    };
    

    [======]

    标准库定义的函数对象

    标准库已经定义了一组表示算术运算符、关系运算符、逻辑运算符的类,每个类各自定义了一个执行类名称对应操作的运算符。
    比如,plus类定义了operator()用于加法运算;modulus类定义了opeartor()用于求%运算;equal_to类定义了operator()用于判断2个参数是否相等,等等。
    这些类都被定义成了模板形式,例如,其使用方法见下:

    #include <functional>
    
    // 定义函数对象
    plus<int> intAdd; // 可执行加法运算的函数对象
    negate<int> intNegate; // 可对int值加上负号的函数对象
    greater<int> comp1; // 执行比较运算的函数对象
    less<int> comp2; // 执行比较运算的函数对象
    
    // 调用函数对象
    int sum = intAdd(1,2); // 使用intAdd::operator(int,int)对1和2求和, sum值为3
    sum = intNegate(intAdd(3,4)); // sum值为-7
    bool res1 = comp1(1,2); // false
    bool res2 = comp2(1,2); // true
    

    在算法中使用标准库函数对象

    比如,使用sort进行排序

    vector<string> vec;
    // ... 向vec插入数据
    // 传入临时函数对象, 用于2个string对象的 > 比较运算
    sort(vec.begin(), vec.end(), greater<string>()); // 降序排序
    // 传入临时函数对象, 用于2个string对象的 < 比较运算
    sort(vec.begin(), vec.end(), less<string>());    // 升序排序
    

    可调用对象与function

    C++可调用的对象包括:函数、函数指针、lambda表达式、bind创建的对象、重载了函数调用运算符(operator())的类。

    • 调用形式
      可调用的对象也有自己的类型,2个不同类型的可调用对象可能共享一种调用形式(call signature)。一种调用形式对应一个函数类型,指明了返回类型、参数列表,如:
    int (int, int)
    返回类型为int;参数列表int,int,即接受2个int参数。
    
    • 不同类型可调用参数,具有相同的调用形式
      多种不同类型的可调用参数,可能具有相同的调用形式。例如,
    // 普通函数
    int add(int i, int j) { return i + j; }
    // lambda表达式
    auto s = [](int i, int j) -> int{ return i + j; }
    // 函数对象类
    struct sum {
    	int operator()(int i, int j) {
    		return i + j;
    	}
    };
    

    标准库function类型

    在C中,当需要一个函数指针时,可以直接传函数或者函数指针,但是无法传lambda表达式或者函数对象类,因为后者并不是函数指针类型。
    C++中的function能解决这个问题,代表任意一种C++可调用对象。
    function是模板,当创建一个具体的function类型时,必须提供额外信息。

    // C的使用函数指针的做法
    typedef int (*AddFunc)(int , int);
    AddFunc handle = add;
    int s = handle(1,2); // 3
    
    // C++使用function的做法
    function<int(int, int)> f1 = add; // 函数指针
    function<int(int, int)> f2 = sum(); // 函数对象
    function<int(int, int)> f3 = [](int i, int j){ return i + j; }; // lambda
    
    int s1 = f1(3, 4); // 7
    int s2 = f2(3, 4); // 7
    int s3 = f3(3, 4); // 7
    

    [======]

  • 相关阅读:
    Javascript Fromdata 与jQuery 实现Ajax文件上传以及文件的删除
    Javascript Fromdata 与jQuery 实现Ajax文件上传
    web.xml配置
    servlet反射、生命周期、接口
    Tomcat
    java--反射原理及操作
    CSS基础--属性选择器、伪类选择器
    webStom常用快捷键备忘
    HTML5--sessionStorage、localStorage、manifest
    ES6
  • 原文地址:https://www.cnblogs.com/fortunely/p/15589880.html
Copyright © 2020-2023  润新知