函数对象是什么

对此,C++标准委员会也认为条件竞争的处理很重要,所以C++标准库提供了更好的处理方法:使用std::call_once函数来处理,其定义在头文件#include<mutex>中。std::call_once函数配合std::once_flag可以实现:多个线程同时调用某个函数,它可以保证多个线程对该函数只调用一次。它的定义如下:

struct once_flag
{
    constexpr once_flag() noexcept;
    once_flag(const once_flag&) = delete;
    once_flag& operator=(const once_flag&) = delete;
};

template<class Callable, class ...Args>
void call_once(once_flag& flag, Callable&& func, Args&&... args);

他接受的第一个参数类型为std::once_flag,它只用默认构造函数构造,不能拷贝不能移动,表示函数的一种内在状态。后面两个参数很好理解,第一个传入的是一个Callable。Callable简单来说就是可调用的东西,大家熟悉的有函数、函数对象(重载了operator()的类)、std::function和函数指针,C++11新标准中还有std::bind和lambda(可以查看我的上一篇文章)。最后一个参数就是你要传入的参数。 在使用的时候我们只需要定义一个non-local的std::once_flag(非函数局部作用域内的),在调用时传入参数即可,如下所示:
 

===========================================================================================================================================================================================================================

作者:小王同学在积累
链接:https://www.zhihu.com/question/441629348/answer/1707068212
来源:知乎
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
 

1. 基本概念

重载函数调用操作符的类,其对象常称为函数对象

② 函数对象使用重载的()时,行为类似函数调用,也叫仿函数

③ 函数对象(仿函数)是一个类,不是一个函数。

2. 特点

① 函数对象在使用时,可以像普通函数那样调用,可以有参数。

② 函数对象超出普通函数的概念,函数对象可以有自己的状态。

③ 函数对象可以作为参数传递

④ 仿函数写法是非常灵活的,可以作为参数进行传递。

#include<iostream>
using namespace std;

class MyAdd
{
public:
    int operator()(int v1, int v2)
    {
        return v1 + v2;
    }
};

//1、函数对象在使用时,可以像普通函数那样调用,可以有参数,可以有返回值
void test01()
{
    MyAdd myAdd;
    cout << myAdd(10,10) << endl;
}

//2、函数对象超出普通函数的概念,函数对象可以有自己的状态
class MyPrint
{
public:
    MyPrint()
    {
        this->count = 0;
    }

    void operator()(string test)
    {
        cout << test << endl;
        this->count++;
    }
    int count;  //内部自己状态
};

void test02()
{
    MyPrint myPrint;
    myPrint("hellow world");
    myPrint("hellow world");
    myPrint("hellow world");
    myPrint("hellow world");

    cout << "myPrint调用次数为:" << myPrint.count << endl;
}

//3、函数对象可以作为参数传递

void doPrint(MyPrint &mp, string test)
{
    mp(test);
}

void test03()
{
    MyPrint myPrint;
    doPrint(myPrint,"hellow world");
}

int main() 
{
    test01();
    test02();
    test03();

    system("pause");

    return 0;
}

运行结果:

  • 20
  • hellow world
  • hellow world
  • hellow world
  • hellow world
  • myPrint调用次数为:4
  • hellow world
  • 请按任意键继续. . .

[ 完 ]

===========================================================================================================================================================================================================================

什么是函数对象?

刚开始看到这个名字,还以为是函数呢,谁知道大错特错!

      顾名思义,就是在某种方式上表现得象一个函数的对象。典型地,它是指一个类的实例,这
个类定义了应用操作符operator()。 
  
函数对象是比函数更加通用的概念,因为函数对象可以定义跨越多次调用的可持久的部分
(类似静态局部变量),同时又能够从对象的外面进行初始化和检查(和静态局部变量不同)。
例如: 
  
class Sum { 
    int val; 
public: 
    Sum(int i) :val(i) { } 
    operator int() const { return val; }        // 取得值 
  
    int operator()(int i) { return val+=i; }    // 应用 
}; 
  
void f(vector v) 

    Sum s = 0;  // initial value 0 
    s = for_each(v.begin(), v.end(), s);    // 求所有元素的和 
    cout << "the sum is " << s << "/n"; 
     
    //或者甚至: 
    cout << "the sum is " << for_each(v.begin(), v.end(), Sum(0)) << 
"/n"; 

  
注意一个拥有应用操作符的函数对象可以被完美地内联化(inline),因为它没有涉及到
任何指针,后者可能导致拒绝优化。与之形成对比的是,现有的优化器几乎不能(或者完全
不能?)将一个通过函数指针的调用内联化。 
  
在标准库中,函数对象被广泛地使用以获得弹性。 

它有什么用呢?

函数对象用处相当大,我也是一新手,不能一一道来,只能就我知道的说说。

一、可以用(而且提倡、建议)函数对象来代替函数指针。

函数指针用的好好的,干吗有造出来一个函数对象呢?C++ 疯了?没有疯,明智的很。因为函数对象与函数指针相比有N多优点:

(1)、函数对象可以在内部修改而不用改动外部接口。。。。。。这优点很明显了吧。(什么灵活啊、富有弹性啊等等)

(2)、函数对象具备有存储先前调用结果的数据成员。它可以在使用前保存,使用后你想取出都没问题。而普通函数则需要将先前调用的结果存储在全程或者本地静态变量中,但是全程或者本地静态变量有某些我们不愿意看到的缺陷。

(3)、函数对象可内联编译,性能好。用函数指针几乎不可能。

二、函数对象所完成的功能不只是“函数的功能”

与原有的函数指针相比,它可以很好的用于类方法。而函数指针差矣。现举例说明:

普通函数指针:
typedef int (*PFUNC)(char*, int);
int MyFunc(char* name, int value);
PFUNC pMyFunc = MyFunc;
int ret = (*pMyFunc)("hello", 66);

类方法指针:
typedef int (CMyClass::*pfunc)(char*, int); //必须是知道要指向的是CMyClass类的方法
int CMyClass::MyFunc(char* name, int value);
CMyClass me;
pfunc pMyFunc = me.MyFunc;
(*pMyFunc)("hello", 66); // 错误!如果MyFunc中用到了成员变量就访问无效
(me.*pMyFunc)("hello", 66); // 必须加 me.*才能正确调用

函数类:
class CFunc
{
public:
typedef int (CObject::*ftype)(char*, int); //函数类型
CFunc(CObject* _obj, ftype _func):obj(_obj),func(_func) {};
CObject* obj;
ftype func;
int operator()(char* name, int value)
{
return (obj->*func)(name, value); // operator() 调用原对象的类方法
};
};

class CMyClass
{
public:
int CMyClass::MyFunc(char* name, int value)
{
return strlen(name)+value;
};
};

CMyClass me;
CFunc pMyFunc((CObject*)&me, (CFunc::ftype)CMyClass::MyFunc);
pMyFunc("myname", 88);

三、函数对象通常不定义构造函数和析构函数,所以在构造和析构时不会发生任何问题,避免了函数调用的运行时问题。

四、模版函数对象使函数对象具有通用性,这也是它的优势之一。

class Generic
{
public:
template <class T> T operator() (T t) const {return -t;}
};
int main()
{
Generic negate;
cout<< negate(5.3333); // double
cout<< negate(10000000000i64); // __int64
}

如果用普通的回调函数实现上述的灵活性是相当困难的。

如何使用函数对象?
http://blog.csdn.net/winc_co/archive/2007/06/09/1645318.aspxs

===========================================================================================================================================================================================================================

=======================================================================

函数对象

函数对象

定义

定义了operator()的对象就是函数对象。

函数的封装

可以使用std::function对函数(指向函数的指针)、lambda表达式、bind表达式、函数对象、指向成员函数的指针、指向成员变量的指针;

简单示例

#include <functional>
#include <iostream>

int fun(int a)
{
    std::cout << a << std::endl;
    return 0;
}

int main()
{
    std::function<int(int)> func_wrap(fun);

    func_wrap(1);
    return 0;
}

偏函数

Partial Function Application.

偏函数是指固定多元函数的部分参数,得到一个接受部分参数的函数的转换过程;

std::bind用来实现偏函数;

这是什么

在 C++ 中应该怎么封装一个函数?

C++ 是面向对象的,而函数作为一个地址,本身不是对象;并且类的普通成员函数,不能直接作为一个普通函数去使用(其名字本身不是一个函数指针)。

C++ 是利用仿函数来达到函数封装函数的目的的。仿函数能够像函数那样使用,同时仿函数本身是一个对象,能够携带一些附加参数。

附 1:调用一个对象的成员函数有哪些方式

#include <iostream>

class Tree
{
public:
    void start()
    {
        print ();
    }
private:
    void print()
    {
        std::cout << "print" << std::endl;
    }
};

int main ()
{
    Tree tree;
    tree.start ();
}

通过对象直接调用这个函数,是最常见的方法。

那么是否和普通的函数一样,能够通过函数指针去调用成员函数呢?

以一个例子说明这个问题

pthread 要求传入一个函数指针,例如:

void *func(void *)
{
    // blank
}

int main()
{
    pthread_t tid;
    pthread_create (& tid, NULL, func, NULL);
    return 0;
}

但是 C++ 的实际场景中,更多的函数是以类的成员函数的形式出现的。类的成员函数,仍然是一个函数,但是其名字不能直接作为普通函数使用,因为其隐含了 this 指针。

#include <pthread.h>
#include <stdio.h>

class Tree
{
public:
    void start()
    {
        pthread_t tid;
        pthread_create (& tid, NULL, doStart, NULL);
        return ;
    }
private:
    void *doStart(void *)
    {
        printf ("start\n");
        return NULL;
    }
};

int main()
{
    Tree tree;
    tree.start();
}

这个例子中,在成员函数中直接把成员函数作为函数指针使用,编译会报错

test.cpp: In member function ‘void Tree::start()’:
test.cpp:10:38: error: invalid use of non-static member function ‘void* Tree::doStart(void*)’
   10 |         pthread_create (& tid, NULL, doStart, NULL);
      |                                      ^~~~~~~
test.cpp:14:11: note: declared here
   14 |     void *doStart(void *)
      |           ^~~~~~~

这个例子中,我们需要实现的语义是创建一个线程,这个线程能直接调用类的成员函数。由于成员函数可能是外部不可见的,因此很可能的场景是在类内部创建一个线程,这个线程调用内部的成员函数(和例子中的一样)。

正如提示的那样,我们需要把这个函数变成类的静态成员函数才能直接传给 pthread_create ,但是这样的话就破坏掉了 C++ 的面向对象的形式,譬如静态函数中如果要访问对象的成员,则只能显式的写一个 this 指针参数传递过去,如下:

#include <pthread.h>
#include <stdio.h>

class Tree
{
public:
    void start()
    {
        pthread_t tid;
        pthread_create (& tid, NULL, doStart, this);
        return ;
    }
    void print()
    {
        printf ("hello world\n");
        return ;
    }
private:
    static void *doStart(void *arg)
    {
        Tree *pThis = reinterpret_cast<Tree*>(arg);
        pThis->print();
        return NULL;
    }
};

int main()
{
    Tree tree;
    tree.start();
}

此时编译能够通过,但是破坏了 C++ 的形式(使用了显式的 this 指针)。

一个解决方案

将类的成员函数

#include <functional>
#include <thread>
#include <iostream>

class Tree
{
public:
    void start()
    {
        std::function<void()> func([this]{this->doStart();});
        std::thread trd(func);
        trd.join();
    }
private:
    void doStart()
    {
        for (int i = 0; i < 5; ++i)
        {
            std::cout << i << std::endl;
        }
        return ;
    }
};

int main()
{
    Tree tree;
    tree.start();
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值