C++中的函数对象指针

3

我想向一个类传递一个函数对象,这个类将使用函数对象在类内部执行一些工作。

但问题是,我不知道将要传入什么函数对象。所以我想到,在类中定义一个void *指针,这个指针将被初始化为将要传入的函数对象。

代码如下:

class A 
{
    public:
        //...
        void doSomeJob(int x)
        {
            (*functor)(x); //use the function object to process data x
        }

    private:
        //...
        void *functor; //function object's pointer will be assigned to this pointer
};

但是代码不起作用。 我猜,不能以那种方式使用 void *functor
我知道我可以使用模板类完成工作,但我的问题是,我是否仍然可以使用函数对象指针来完成工作,如何实现? 附言 为了让我的问题更清晰,可能会有几个函数对象,它们之间的区别在于它们如何处理数据,我不知道将传递哪个函数对象,但我知道它们每个都需要一个int参数。
正如一些答案所述,我可以通过函数指针来完成工作,但是函数对象比函数指针具有更多的实用功能,例如状态,这就是我要使用的东西。

阅读这篇文章 - Samuel Harmer
4个回答

9

如果函数对象的类型没有被存储在可访问的位置,那么你不能在调用站点调用该函数对象。有两个选项:如果你可以使用C++11或boost,你可以使用std::function resp。boost::function:

class A
{
public:
  // ...
  void doSomeJob(int x)
  {
    functor(x);
  }
private:
  std::function<void(int)> functor; // or boost::function when using boost
};

这里,类型(以隐式形式)存储在function模板的机制中。

否则,如果你要求所有传递的函数对象都是从特定基类派生的类类型,则可以创建一个抽象基类:

struct AbstractFunctor
{
  virtual void operator()(int) = 0;
};

class A
{
public:
  // ...
  void doSomeJob(int x)
  {
    (*functor)(x);
  }
private:
  AbstractFunctor* functor; // or boost::function when using boost
};

函数对象的类型存储在虚拟表中。

如果您确实无法使用boost,则也可以自己编写类似的解决方案。关键词是“类型擦除”,它基本上通过从已知基类(如我的第二个解决方案中)动态生成派生对象来工作,该派生对象知道您对象的类型并可以调用它。可以按照以下大致步骤完成(未经测试的代码):

class int_function
{
private:
  struct abstract_forward
  {
    virtual void call(int) = 0;
    virtual abstract_forward clone() const = 0;
    virtual ~abstract_forward() {}
  };
  template<typename Functor> struct forward: abstract_forward
  {
    forward(Functor f): func(f) {}
    void call(int i) { func(i); }
    abstract_forward clone() const { return new forward<Functor>(func); }
    Functor func;
  };
public:
  template<typename Functor> int_function(Functor f)
  {
    forwarder = new forward<Functor>(f);
  }
  int_function(int_function const& other)
  {
    forwarder = other.forwarder->clone();
  }
  int_function& operator=(int_function const& other)
  {
    abstract_forward* newfwd = other.forwarder->clone();
    delete forwarder;
    forwarder = newfwd;
  }
  ~int_function()
  {
    delete forwarder}
  }
  void operator()(int i)
  {
    forwarder->call(i);
  }
private:
  abstract_forward* forwarder;
};

class A
{
public:
  void doSomeJob(int x)
  {
    functor(x);
  }
private:
  int_function functor;
};

我不使用C++11或boost。因此,我不能像我的代码中那样通过简单指针来完成它,对吗? - Alcott
@Alcott:我的第二个解决方案没有使用任何 C++ 标准化之前不存在的东西。但是,它对您的函数对象施加了一些限制(类必须派生自“AbstractFunctor”)。顺便问一下,您不能使用 boost 的原因是什么? - celtschk
嗯,我不使用boost,因为我不熟悉这个库,它是否流行或已经标准化了? - Alcott
非常感谢您详细的回答。顺便问一下,类内结构体 abstract_forward 的析构函数有什么问题吗? - Alcott
@Alcott:嗯,Boost的很多部分(特别是boost::function)已经基本上被纳入了C++11。总体来说,Boost库具有高质量和可移植性,而且它们还拥有宽松的许可证。此外,Boost实际上是一组库,因此您不必学习复杂的专业库,例如boost::graph或boost::spirit,只需使用简单且通常有用的库,例如boost::function、boost::optional或智能指针即可。 - celtschk
显示剩余2条评论

2
void *functor; //function object's pointer will be assigned to this pointer

这不是函数指针。

你需要的是这个:

void (*functor)(int); //function pointer

更好的方法是这样写(在C++11中):
#include <functional> //must include this 

std::function<void(int)> functor;

//then use it as:
functor(x);  //not (*functor)(x)

他说的是函数对象,而不是函数。 - celtschk
@celtschk:他似乎有些困惑。无论如何,std::function将解决那个问题。 - Nawaz
@celtschk,是的,我的意思是“函数对象”,而不是“函数指针”。 - Alcott

1

1

C++ 对其类型非常严格,因此您不能仅使用 void* 作为函数指针。指针必须是实际的函数指针才能调用它。

你是说你不知道将传递什么函数对象吗?在那个例子中,您知道它以 intint& 作为参数,并且可能返回 void,因此您可以将函数指针存储为:

void (*func)(int);

如果你想说你想要能够存储类成员函数,或者重载operator()的类实例,那么你可以使用C++11中的std::functionstd::bind,或者boost::functionboost::bind

boost::function<void (int)> func;

是的,我的意思是“函数对象”,而不是“函数指针”。我不知道将传递哪个函数对象,但我知道传递的任何函数对象都将使用一个“int”参数,因此我想用指向函数对象实例的指针初始化“void * functor”。 - Alcott
@Alcott,使用 std::functionboost::function)绝对是正确的选择。只要返回类型和参数匹配,它就能处理任何可调用对象(而不仅仅是函数)。阅读我提供的链接中的文档,它们肯定是非常有用的库。 - Paul Manta

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接