16

我正在尝试设置一个在执行期间根据一组用户参数设置的函数指针。我想让函数指针指向一个非静态成员函数,但我找不到怎么做。

我见过的例子说这只能用静态成员函数来完成,或者在直接 C 中使用全局变量。

一个简化的例子如下:

    class CA
    {
    public:
        CA(void) {};
        ~CA(void) {};
        void setA(double x) {a = x; };
        void setB(double x) {b = x; };

        double getA(const double x) {return x*a; };
        double getB(const double x) {return x*b; };

        void print(double f(const double), double x) {
            char cTemp[256];
            sprintf_s(cTemp, "Value = %f", f(x));
            std::cout << cTemp;
        };

    private:
        double a, b;
    };

实现部分是

CA cA;
cA.setA(1.0);
cA.setB(2.0);

double (*p)(const double);

if(true) {
    p = &cA.getA;  //'&' : illegal operation on bound member function expression
} else {
    p = cA.getB;  //'CA::getB': function call missing argument list; use '&CA::getB' to create a pointer to member
                  //'=' : cannot convert from 'double (__thiscall CA::* )(const double)' to 'double (__cdecl *)(const double)'
}

cA.print(p, 3.0);

那么如何让 p 指向“getA”或“getB”,以便“打印”仍然可以使用它。

据我所知,建议是使用 boost 或 std::bind 但我对这两种方法都没有经验。我希望我不需要深入研究这些,我只是错过了一些东西。

编译器 MSVC++ 2008

4

3 回答 3

16

不要忘记成员函数接受隐式this参数:因此,接受 a 的成员函数double不能与接受 a 的非成员(自由)函数相同double

// OK for global functions
double (*p)(const double);

// OK for member functions
double (CA:*p)(const double);

调用它们的方式也不同。首先,对于成员函数,您需要一个对象来调用它们(其地址最终将绑定到this函数调用中的指针)。其次,您需要使用.*运算符(->*如果您通过指针执行调用,则需要使用运算符):

p = &CA::getA;
CA cA;
(cA.*p)();

始终如一地,您将不得不更改对 function 的定义print()

    #include <iostream>

    void print(double (CA::*f)(const double), double x) 
    {
        // Rather use the C++ I/O Library if you can...
        std::cout << "Value = " << (this->*f)(x);
    };

所以最后,这就是你应该如何重写你的main()函数:

int main()
{
    CA cA;
    cA.setA(1.0);
    cA.setB(2.0);

    double (CA::*p)(const double);

    if (true) // Maybe use some more exciting condition :-)
    {
        p = &CA::getA;
    } 
    else {
        p = &CA::getB;
    }

    cA.print(p, 3.0);
}
于 2013-02-23T01:25:53.967 回答
8

编译问题

该答案侧重于问题中提出的编译问题。我不建议将其作为解决方案实施。

指向成员函数的指针最好用typedefs 和宏来处理。

这是调用成员函数的宏:

#define CALL_MEMBER_FN(object, ptrToMember)  ((object).*(ptrToMember))

资料来源: [33.6] 使用指向成员函数的指针调用成员函数时,如何避免语法错误?, C++ 常见问题解答

(object).*(ptrToMember)这使您在任何时候希望通过指针调用成员函数时都不必记住丑陋的语法。

在你的class,声明一个typedef被调用的CAGetter,这将使变量声明更简单:

class CA
{
public:    
    typedef double (CA::*CAGetter)(const double x);

然后你可以print()很简单地声明你的函数:

    void print(CAGetter f, double x)

正文也简洁明了:

    {
        std::cout << "value = " << CALL_MEMBER_FN(*this, f)(x) << '\n';
    }

示例用法:

CA a;
a.setA(3.1);
a.setB(4.2);

// Using a variable...
CA::CAGetter p = &CA::getA;
a.print(p, 1);

// without a variable
a.print(&CA::getB, 1);

// Calling the functions from outside the class...
std::cout << "From outside (A): " << CALL_MEMBER_FN(a, p)(10) << std::endl;
std::cout << "From outside (B): " << CALL_MEMBER_FN(a, &CA::getB)(10) << std::endl;

设计问题

将指向成员函数的指针传递给同一类的实例的方法是一种设计味道(通常不会将成员变量传递给方法,这没有什么不同)。这个问题中没有足够的信息来解决底层设计问题,但这个问题可能可以通过单独print()的方法、成员变量或继承和多态来解决。

于 2013-02-23T01:51:58.963 回答
0

您可以使用指向方法的指针:

class CA
{
    public:
        typedef double (CA::*getter)( double );
        CA(void) {};
        ~CA(void) {};
        void setA(double x) {a = x; };
        void setB(double x) {b = x; };

        double getA(const double x) {return x*a; };
        double getB(const double x) {return x*b; };

        void print(getter f, double x) {
            char cTemp[256];
            sprintf(cTemp, "Value = %f", (this->*f)(x));
            std::cout << cTemp;
        };

    private:
        double a, b;
};

int main()
{
    CA cA;
    cA.setA(1.0);
    cA.setB(2.0);

    CA::getter p;

    if(true) {
            p = &CA::getA;  
    } else {
            p = &CA::getB; 
    cA.print( p, 3.0 );
}

或者使用 boost::bind

class CA
{
    public:
        typedef boost::function<double( double )> getter;
        CA(void) {};
        ~CA(void) {};
        void setA(double x) {a = x; };
        void setB(double x) {b = x; };

        double getA(const double x) {return x*a; };
        double getB(const double x) {return x*b; };

        void print(getter f, double x) {
            char cTemp[256];
            sprintf(cTemp, "Value = %f", f(x));
            std::cout << cTemp;
        };

    private:
        double a, b;
};

int main()
{
    CA cA;
    cA.setA(1.0);
    cA.setB(2.0);

    CA::getter p;

    if(true) {
            p = boost::bind( &CA::getA, &cA, _1 );
    } else {
            p = boost::bind( &CA::getB, &cA, _1 );
    }
    cA.print( p, 3.0 );
}
于 2013-02-23T01:35:48.067 回答