如何从静态成员函数调用指向成员函数的指针?

Posted

技术标签:

【中文标题】如何从静态成员函数调用指向成员函数的指针?【英文标题】:How to invoke pointer to member function from static member function? 【发布时间】:2014-10-28 09:25:16 【问题描述】:

我需要获取一个由标准函数指针调用的成员函数,所以我尝试抽象如下:

class Sample 
public:
    virtual void doSomething(void) = 0;
;


class A : public Sample 
    void doSomething(void);     // details omitted
;

class B : public Sample 
    void doSomething(void);     // details omitted
;


class Executor 
public:
    Executor(Sample *sample)
     : func(&sample->doSomething)
    
    

    static void *execute(void *data) 
        Executor *pX = data;

        (pX->*func)();          // error invalid access of func from static function

        (pX->*pX->func)();      // error pointer to member type 'void (Sample::)()'
                                //       incompatible with object type 'Executor'
    

private:
    void (Sample::*func)(void);
;



int main(void) 
    A   myA;
    B   myB;
    Executor x0(&myA);
    Executor x1(&myB);

    externallyInvoke(&Executor::execute, &x0);
    externallyInvoke(&Executor::execute, &x1);

externallyInvoke是一个Linux系统调用,它接受一个函数指针和一个数据指针。 我想使用静态成员函数和 this 指针作为数据。

... 我不希望像 AB 这样的类有静态成员。所以我的想法是创建一个类似Sample 类的接口,由AB 扩展。

我的问题是我不知道如何从 Executor::execute 函数内部调用指向成员函数的指针。

【问题讨论】:

无法从静态函数访问成员变量 - 有什么不清楚的地方? 【参考方案1】:

问题是execute 中需要两个对象——一个是Executor 的实例,它将提供func,另一个是Sample 的实例(派生自)Sample 987654325@ 将被调用。所以你必须在Executor 中存储对象,而不是函数:

class Executor 
public:
    Executor(Sample *sample)
     : obj(sample)
    
    

    static void *execute(void *data) 
        Executor *pX = static_cast<Executor*>(data);

        pX->obj->doSomething();
    

private:
    Sample *obj;
;


int main()  // note that `void main()` is not legal C++
    A   myA;
    B   myB;
    Executor x0(&myA);
    Executor x1(&myB);

    externallyInvoke(&Executor::execute, &x0);
    externallyInvoke(&Executor::execute, &x1);

指向成员函数的指针(例如您原来的void (Sample::*func)())标识类中的函数,但不存储对象。您仍然需要提供一个来调用该函数。

【讨论】:

不应该是x0(myA)x0(&amp;myA) @Wimmel 是的,当然,我在问题中修复之前从 OP 复制了它。谢谢。 @Angew 我显然是想重新发明一个仿函数,但是您的解决方案非常干净,因此:可爱 :) THX【参考方案2】:

如果你想与外部系统调用交互,你基本上必须自己重新发明std::function。没问题,在 Stack Overflow,我们是重塑现有技术的大师。所以...

一、界面:

struct FunctionStateBase

    virtual ~FunctionStateBase() 
    virtual void Invoke() = 0;
;

extern "C" void InvokeAndDelete(void * data)

    auto state = static_cast<FunctionStateBase *>(data);
    state->Invoke();
    delete state;

这是你如何使用它:

externallyInvoke(&InvokeAndDelete, MakeFunction(&A::doSomething, &myA));

现在我们需要实现MakeFunction

template <typename> struct FunctionState;

template <typename C, typename R>
struct FunctionState<R (C::*)()> : FunctionStateBase

    R (C::ptmf_*)();
    C * obj_;

    FunctionState(R (C::ptmf*)(), C * obj) : obj_(obj), ptmf_(ptmf) 

    virtual void Invoke()  (C->ptmf_)(); 
;

template <typename C, typename R>
FunctionState<R (C::*)()> MakeFunction(R (C::*ptmf)(), C * obj)

    return new FunctionState<R (C::*)()>(ptfm, obj);

此时我们正在手动管理函数包装器的生命周期,请注意InvokeAndDelete 实际上取得了函数状态的所有权。在适当的 C++ 中,我们会将整个系统调用调用包装在一个类中,该类将在内部封装生命周期管理。

您可以为接受参数的成员函数添加进一步的特化;您只需要在状态中存储参数的副本。

【讨论】:

【参考方案3】:

您还需要传递Sample 的实例来调用函数(因为它是指向Sample 成员的指针)。有几种方法可以带来实例。您可以使其成为Executor 的成员,将std::pair* 作为data 传递,或者您可以将函数指针和实例组合为仿函数。这是后者的基于 lamda 的方法。 lamda 的优点是用途更广。可以做的不仅仅是调用一个类的一个成员。作为奖励,这种方法不会避免可见性规则,尽管这意味着 doSomething 可能不是私有的(或者必须通过父指针调用)。

template<class F>
class Executor 
    F f;
public:
    Executor(F f): f(f)
    static void *execute(void *data) 
        Executor<F> *pX = static_cast<Executor<F>*>(data);
        pX->f();
        return this; // not quite sure what you intend to return, but just to make this a well formed function...
    
;


int main() 
    A   myA;
    B   myB;
    auto callback0 = [myA]
        myA.doSomething();
    ;
    auto callback1 = [myB]
        myB.doSomething();
    ;
    Executor<decltype(callback0)> x0(callback0);
    Executor<decltype(callback1)> x1(callback1);

    externallyInvoke(&Executor::execute, &x0);
    externallyInvoke(&Executor::execute, &x1);

【讨论】:

func 是成员,不能在static 方法中使用。 @user657267 啊,明白了。修正了答案。 如何解决这个问题?他想通过static 方法调用成员函数指针,这根本不可能,使其public 没有任何改变。 @user657267,该死的不是我的日子。我没有注意到它不仅是一个成员,而且还是一个指向另一个类的成员函数的指针。

以上是关于如何从静态成员函数调用指向成员函数的指针?的主要内容,如果未能解决你的问题,请参考以下文章

指向任何类类型的非静态成员函数的 C++ 函数指针

成员函数指针和指向静态成员函数的指针

关于虚函数,类的内存分布以及类的成员函数调用原理

从 C++ 中的共享库调用指向列表的静态指针

成员变量和成员函数分开存储

C++之静态