• 函数调用运算符


    如果类重载了函数调用运算符,则可以像使用函数一样使用该类的对象,因为这样的类同时也能存储状态,所以与普通函数相比它们更加灵活。

    struct absInt{
    	int operator()(int val) const{
    		return val < 0 ? -val : val;
    	}
    };
    

    上面的类只定义了一种操作:函数调用运算符,它负责接受一个int类型的形参,然后返回该实参的绝对值。

    int i = -42;
    absInt absObj;
    int ui = absObj(i);	//i被传递给absObj.operator()
    

    即使 absObj是一个对象而非函数,也能调用该对象,调用对象实际上是在运行重载的调用运算符。

    函数调用运算符必须是成员函数,一个类可以定义多个不同版本的调用运算符,相互之间在参数数量和参数类型上应该有所区别。

    如果类定义了调用运算符,则该类的对象称作函数对象,因为可以调用这种对象,所以这些对象的行为像函数一样。

    含有状态的函数对象类

    函数对象除了 operator() 之外也可以包含其他成员。

    class PrintString{
    public:
    	PrintString(ostream &o = cout,char c = ' '):os(o),sep(c){ }
    	void operator()(const string &s)const{os<<s<<sep;}
    private:
    	ostream &os;
    	char sep;
    };
    
    

    使用:

    PrintString printer;
    printer(s);		//cout中打印s,后面跟一个空格
    PrintString errors(cerr,'
    ');
    errors(s);		//cerr中打印s,后面跟一个换行符
    
    

    函数对象通常是作为泛型算法的实参:

    for_each(vs.begin(),vs.end(),PrintString(cerr,"
    "));
    
    

    lambda 是函数对象

    编写了一个 lambda 后,编译器将该表达式翻译成一个未命名类的未命名对象。

    stable_sort(words.begin(),words.end(),[](const string &a,const string &b)
    {return a.size() < b.size();});
    
    

    其行为类似下面这个类的一个未命名对象:

    class ShorterString{
    public:
    	bool operator()(const string &a,const string &b)
    	{return a.size() < b.size();}
    };
    
    

    使用上面的类重写 stable_sort :

    stable_sort(words.begin(),words.end(),ShorterString());
    
    

    表示 lambda 及相应捕获行为的类

    auto wc = find_if(words.begin(),words.end(),[sz](const string &a)
    	{return a.size() > = sz;})
    
    

    该 lambda 表达式产生的类将形如:

    class SizeComp
    {
    	SizeComp(size_t n):sz(n) { }
    	bool operator()(const string &s)const {return s.size() >= sz;}
    private:
    	size_t sz;
    };
    
    auto wc = find_if(words.begin(),words.end(),SizeComp(sz))
    
    

    标准库定义的函数对象

    标准库定义了一组表示算术运算符、关系运算符和逻辑运算符的类,每个类分别定义了一个执行命名操作的调用运算符。

    plus<int> intAdd;	//可执行int加法的函数对
    negate<int> intNegate;	//可对int值取反的函数对象
    int sum = intAdd(10,20);	//sum=30
    sum = intAdd(10,intNegate(10));	//sum=0
    
    

    定义在 functional 头文件中的函数对象:

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

    // 传入一个临时函数对象用于执行两个 string 对象的比较运算
    sort(svec.begin(),svec.end(),greater<string>());
    

    标准库规定其函数对象对于指针同样适用:

    vector<string *> nameTable; //指针的vector
    //错误:nameTable 中的指针彼此之间没有任何关系,所以 < 将产生未定义的行为
    sort(nameTable.begin(),nameTable.end(),[](string *a,string *b){return a < b;}) ;
    //正确,标准库规定指针的 less 定义是良好的
    sort(nameTable.begin(),nameTable.end(),less<string*>());
    

    可调用对象与 function

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

    可调用对象也有类型,labmda 有自己唯一的未命名类型,函数及函数指针的类型由其返回值类型和实参类型决定。两个不同类型的可调用对象却可能共享同一种调用形式,调用形式指明了返回类型以及传递给调用的实参类型,一种调用形式对应一个函数类型:

    int (int,int)	//是一个函数类型,它接受两个int,返回一个int
    

    不同的类型可能具有相同类型的调用方式

    int add(int i,int j){return i + j;}
    auto mod = [](int i,int j){return i % j;}
    struct divide{
    	int operator()(int denminator,int divisor){
    		return denminator / divisor;
    	}
    };
    
    
    

    尽管这些可调用对象对其参数执行了不同的算术运算,尽管它们的类型各不相同,但是共享一种调用形式:

    int (int,int)
    
    

    构建实现不同运算的函数表:

    map<string,int(*)(int,int)> binops;
    binops.insert({"+",add});
    
    binops.insert({"%",mod});	//错误,mod不是函数指针
    
    

    标准库 function 类型

    function 定义在 functional 头文件中:

    function 是一个模板,创建具体的 function 类型时需要提供额外的信息。

    function<int(int,int)>
    
    
    function<int(int,int)> f1 = add;		//函数指针
    function<int(int,int)> f2 = divide;		//函数对象类的对象
    function<int(int,int)> f3 = [](int i,int j){return i * j;}		//lambda
    
    //调用
    cout<<f1(4,2)<<endl;
    cout<<f2(4,2)<<endl;
    cout<<f3(4,2)<<endl;
    
    

    使用 function 重新定义上面的函数表:

    map<string,function<int(int,int)>> binops = 
    {
    	{"+",add},		//函数指针
    	{"-",std::minus<int>()},		//标准库函数对象
    	{"/",divide},		//自定义函数对象
    	{"*",[](int i,int j){return i * j;}},			//未命名lambda表达式
    	{"%",mod},				//命名lambda表达式
    };
    
    

    调用:

    binops["+"](10,5);
    binops["-"](10,5);
    binops["/"](10,5);
    binops["*"](10,5);
    binops["%"](10,5);
    
    

    重载的函数与 function

    不能直接将重载函数的名字存入 function 类型的对象中。

    int add(int i,int j){return i + j;}
    Sales_data add(const Sales_data&,const Sales_data&);
    map<string,funciton<int(int,int)>> binops;
    binops.insert({"+",add});		//错误,不能区分是哪个add 
    
    

    解决上面问题的有效途径是存储函数指针而非函数名字:

    int (*fp) (int,int) = add;
    binops.insert("add",fp);	//正确,fp指向正确的add版本
    
    

    同样,也可以使用 lambda 来指定希望使用的 add 版本:

    binops.insert({"+"},[](int a,in b){return add(a,b);});
    
  • 相关阅读:
    解释中国经济奇迹的5本书
    《门口的野蛮人》三部曲,美国的宝万之争专业户
    3星|《城市更新》:欧美东亚中国老城区改造的资料与案例汇编
    3.5星|《哈佛商业评论》2018年第11期:机场的书店很危险,出差的高管可能会被商业新书主张吸引
    3星|《好文案一句话就够了》:10年前一些日本经典广告文案
    5星|林毅夫《解读中国经济》:完美解释中国经济奇迹和现存问题
    iOS:Masonry 英文原档介绍
    iOS:Masonry介绍与使用
    iOS :学习新技术途径和sizeClasses屏幕适配
    iOS:XMPP即时聊天知识
  • 原文地址:https://www.cnblogs.com/xiaojianliu/p/12496502.html
Copyright © 2020-2023  润新知