C++ 你如何传递成员函数指针?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/130322/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-27 13:05:16  来源:igfitidea点击:

How do you pass a member function pointer?

c++classfunctionpointersmember

提问by Matt Pascoe

I am trying to pass a member function within a class to a function that takes a member function class pointer. The problem I am having is that I am not sure how to properly do this within the class using the this pointer. Does anyone have suggestions?

我试图将类中的成员函数传递给采用成员函数类指针的函数。我遇到的问题是我不确定如何使用 this 指针在类中正确执行此操作。有没有人有建议?

Here is a copy of the class that is passing the member function:

这是传递成员函数的类的副本:

class testMenu : public MenuScreen{
public:

bool draw;

MenuButton<testMenu> x;

testMenu():MenuScreen("testMenu"){
    x.SetButton(100,100,TEXT("buttonNormal.png"),TEXT("buttonHover.png"),TEXT("buttonPressed.png"),100,40,&this->test2);

    draw = false;
}

void test2(){
    draw = true;
}
};

The function x.SetButton(...) is contained in another class, where "object" is a template.

函数 x.SetButton(...) 包含在另一个类中,其中“对象”是一个模板。

void SetButton(int xPos, int yPos, LPCWSTR normalFilePath, LPCWSTR hoverFilePath, LPCWSTR pressedFilePath, int Width, int Height, void (object::*ButtonFunc)()) {

    BUTTON::SetButton(xPos, yPos, normalFilePath, hoverFilePath, pressedFilePath, Width, Height);

    this->ButtonFunc = &ButtonFunc;
}

If anyone has any advice on how I can properly send this function so that I can use it later.

如果有人对我如何正确发送此功能有任何建议,以便我以后可以使用它。

回答by Commodore Jaeger

To call a member function by pointer, you need two things: A pointer to the object and a pointer to the function. You need both in MenuButton::SetButton()

要通过指针调用成员函数,您需要两件事:指向对象的指针和指向函数的指针。你同时需要MenuButton::SetButton()

template <class object>
void MenuButton::SetButton(int xPos, int yPos, LPCWSTR normalFilePath,
        LPCWSTR hoverFilePath, LPCWSTR pressedFilePath,
        int Width, int Height, object *ButtonObj, void (object::*ButtonFunc)())
{
  BUTTON::SetButton(xPos, yPos, normalFilePath, hoverFilePath, pressedFilePath, Width, Height);

  this->ButtonObj = ButtonObj;
  this->ButtonFunc = ButtonFunc;
}

Then you can invoke the function using both pointers:

然后您可以使用两个指针调用该函数:

((ButtonObj)->*(ButtonFunc))();

Don't forget to pass the pointer to your object to MenuButton::SetButton():

不要忘记将指向您的对象的指针传递给MenuButton::SetButton()

testMenu::testMenu()
  :MenuScreen("testMenu")
{
  x.SetButton(100,100,TEXT("buttonNormal.png"), TEXT("buttonHover.png"),
        TEXT("buttonPressed.png"), 100, 40, this, test2);
  draw = false;
}

回答by Matt Cruikshank

I'd strongly recommend boost::bindand boost::functionfor anything like this.

我强烈推荐boost::bind,并boost::function为这样的事。

See Pass and call a member function (boost::bind / boost::function?)

参见Pass 并调用成员函数 (boost::bind / boost::function?)

回答by Yuanlong Li

I know this is a quite old topic. But there is an elegant way to handle this with c++11

我知道这是一个相当古老的话题。但是有一种优雅的方法可以用 c++11 来处理这个问题

#include <functional>

declare your function pointer like this

像这样声明你的函数指针

typedef std::function<int(int,int) > Max;

declare your the function your pass this thing into

声明你的函数,你将这个东西传递给

void SetHandler(Max Handler);

suppose you pass a normal function to it you can use it like normal

假设您向它传递了一个普通函数,您可以像平常一样使用它

SetHandler(&some function);

suppose you have a member function

假设你有一个成员函数

class test{
public:
  int GetMax(int a, int b);
...
}

in your code you can pass it using std::placeholderslike this

在您的代码中,您可以std::placeholders像这样传递它

test t;
Max Handler = std::bind(&test::GetMax,&t,std::placeholders::_1,std::placeholders::_2);
some object.SetHandler(Handler);

回答by GKelly

Would you not be better served to use standard OO. Define a contract (virtual class) and implement that in your own class, then just pass a reference to your own class and let the receiver call the contract function.

使用标准 OO 会不会更好?定义一个合约(虚拟类)并在你自己的类中实现它,然后只需传递一个对你自己类的引用,让接收者调用合约函数。

Using your example (I've renamed the 'test2' method to 'buttonAction'):

使用您的示例(我已将“test2”方法重命名为“buttonAction”):

class ButtonContract
{
  public:
    virtual void buttonAction();
}


class testMenu : public MenuScreen, public virtual ButtonContract
{
  public:
    bool draw;
    MenuButton<testMenu> x;

    testMenu():MenuScreen("testMenu")
    {
      x.SetButton(100,100,TEXT("buttonNormal.png"), 
              TEXT("buttonHover.png"), 
              TEXT("buttonPressed.png"), 
              100, 40, &this);
      draw = false;
    }

    //Implementation of the ButtonContract method!
    void buttonAction()
    {
      draw = true;
    }
};

In the receiver method, you store the reference to a ButtonContract, then when you want to perform the button's action just call the 'buttonAction' method of that stored ButtonContract object.

在接收器方法中,您存储对 ButtonContract 的引用,然后当您想要执行按钮的操作时,只需调用该存储的 ButtonContract 对象的“buttonAction”方法。

回答by Johannes Schaub - litb

Others have told you how to do it correctly. But I'm surprised no-one told you this code is actually dangerous:

其他人已经告诉你如何正确地做到这一点。但我很惊讶没有人告诉你这段代码实际上是危险的:

this->ButtonFunc = &ButtonFunc;

Since ButtonFunc is a parameter, it will go out of scope when the function returns. You are taking its address. You will get a value of type void (object::**ButtonFunc)()(pointer to a pointer to a member function) and assign it to this->ButtonFunc. At the time you would try to use this->ButtonFunc you would try to access the storage of the (now not existing anymore) local parameter, and your program would probably crash.

由于 ButtonFunc 是一个参数,当函数返回时它将超出范围。你正在取它的地址。您将获得一个类型值void (object::**ButtonFunc)()指向成员函数指针的指针)并将其分配给 this->ButtonFunc。当您尝试使用 this->ButtonFunc 时,您将尝试访问(现在不再存在)本地参数的存储,并且您的程序可能会崩溃。

I agree with Commodore's solution. But you have to change his line to

我同意 Commodore 的解决方案。但是你得把他的台词改成

((ButtonObj)->*(ButtonFunc))();

since ButtonObj is a pointerto object.

因为 ButtonObj 是一个指向对象的指针

回答by Parappa

In the rare case that you happen to be developing with Borland C++Builder and don't mind writing code specific to that development environment (that is, code that won't work with other C++ compilers), you can use the __closure keyword. I found a small article about C++Builder closures. They're intended primarily for use with Borland VCL.

如果您碰巧使用 Borland C++Builder 进行开发并且不介意编写特定于该开发环境的代码(即不能与其他 C++ 编译器一起使用的代码),您可以使用 __closure 关键字. 我发现了一篇关于 C++Builder 闭包小文章。它们主要用于 Borland VCL。