C++: 成员函数地址 (函数指针)

3

我有一个X类,其中有这个方法:

void setRxHandler(void (*h)(int));

我希望您能够将一个在类Y的实例中存在的成员函数传递给它。

void comm_rxHandler(int status);

我尝试了以下操作:

x.setRxHandler(comm_rxHandler) 

但它会得到以下编译错误(我正在使用Qt):
错误:无法找到符合调用“X :: setRxHandler(<未解析的重载函数类型>)”的函数
那么,我该怎么做呢?
我注意到如果我将comm_rxHandler(类Y)声明为静态的,就不会出现错误。但是我想让comm_rxHandler成为一个非静态方法。另外,我希望setRxHandler方法(类X)是通用的而不是特定于类的。因此,我不能将该方法声明为:
setRxHandler(void (Y::*h)(int))

如何做到这一点?你能帮我吗?
谢谢!

在C++0x中,你应该考虑将参数设为std::function<void(int)>,它适用于各种可调用对象。 - Kerrek SB
5个回答

1

C++不支持绑定方法。要通过函数指针调用成员函数,您需要两个东西:类的实例和函数指针。

因此,setRxHandler(void (Y::*h)(int))几乎是正确的。您需要将其声明为:

void setRxHandler(Y*, void (Y::*h)(int));

要调用setRxHandler(),您需要按以下方式传递参数:
Y y;
setRxHandler(&y, &Y::comm_rxHandler);

setRxHandler()方法中,您可以使用以下语法调用函数指针:
void setRxHandler ( Y* y, void (Y::*h)(int) )
{
    // ...
    (y->*h)(0);
    // ...
}

为了实现泛型,您需要将Y参数抽象出来,但这很难做到。请查看Boost.Function,以获取支持此用例及其他许多用例的现有实现。

1

将您的回调函数更改为以下内容:

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

然后您可以使用绑定器:

setRxHandler( std::bind(&class_name::comm_rxHandler, obj) );

(std::functionstd::bind是即将发布的C++标准的一部分。很可能您的编译器已经包含了它们。如果没有,它们可能存在于std::tr1命名空间中。如果其他方法均失败,您可以在boost找到它们 - 这也是它们被发明的地方 - 使用boost::functionboost::bind。)

但是,您还可以将非成员函数或静态函数传递给setRxHandler,以及函数对象(这是std::bind的结果)。

如果您的编译器已经支持lambda函数(也是下一个标准的一部分,但已经受到例如最近版本的GCC和VC的支持),您也可以使用其中之一:

setRxHandler( [](){obj.comm_rxHandler();} );

0

目前,setRxHandler 原型接受一个指向不返回任何内容且带有 int 的函数的指针。正如您所注意到的那样,这对成员函数无效,因为它们不能像普通函数一样被调用(您还必须处理 this 指针,这意味着必须拥有该类的实例才能调用该方法)。

为了使其既适用于成员函数又适用于非特定(通用)函数,您必须创建一个基类,并让您想要使用 setRxHandler 的所有类都从该类派生:

class Base { ... };
class Derived : public Base { ... };

// then for the prototype
void setRxHandler(void (Base::*h)(int)) { ... }
// and you can use setRxHandler for all types that derive from Base, which gives you more control than the second option, which is:

或者使用模板:

template<typename T>
void setRxHandler(void (T::*h)(int)) { ... }

使用模板选项时,您实际上无法控制与 setRxHandler 一起使用的类(除了 RTTI),这可能正是您想要的。


0

你可以为 Y 制作一个基类并使用它(以避免“类特定”),或者使用模板:

template <class T>
setRxHandler(void (T::*h)(int));

但这可能会引发如何使用成员函数的问题(如果有的话,请告诉我们)。


0

正如其他人所提到的,C++不提供此功能。

您可以使用另一种选择libsigc++,这在gtkmm中被广泛使用。例如,在他们的教程中看看this example,以了解如何将指针传递给成员函数。您的示例可能如下所示:

// sigc::slot<void, int> is a 'slot' to hold a function with return type void 
// and 1 int argument.
void setRxHandler(sigc::slot<void, int> slot);
void comm_rxHandler(int status);

//sigc::mem_fun() can convert a member function to a function slot.
x.setRxHandler(sigc::mem_fun(*this, &X::comm_rxHandler));

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