Passing a member function as an argument to a constructor

筅森魡賤 提交于 2019-12-12 09:12:41

问题


I have a button class. I want the button class's constructor to take the function it will call when the button is pressed. This would be easy if the button class was only taking a function from one class, but the purpose of the button's constructor taking a function is that no matter what class the button is created in it will be able to store a pointer to a function in the class it's created in.

To illustrate:

struct Button {
    Button(void (*functionPtr)()) {
        // Store the function pointer to be called later
    }
};

struct SomeClass {
    void LoadFile();

    SomeClass() {
        Button* temp1 = new Button(&LoadFile); // ???
    }
};

struct AnotherClass {
    void SaveFile();

    SomeClass() {
        Button* temp2 = new Button(&SaveFile); // ???
    }
};

How can I make that work?


回答1:


A pointer-to-function and a pointer-to-member-function, despite seeming pretty similar, are actually entirely different beasts.

void (*functionPtr)() is a pointer to a function that takes no arguments and returns void. &AnotherClass::SaveFile is a pointer to a member function of AnotherClass... its type is void (AnotherClass::*)(). Notice that the class name is part of the type, so you can't simply store a pointer-to-member-function to an arbitrary class. Furthermore, to call a pointer-to-member-function, you need an instance pointer - you'd have to store that somehow, but those would have different types too!

What you could do instead in C++11 is use type-erasure:

std::function<void()> callback;

And assign an arbitrary callable to it:

template <typename F>
Button(F cb) 
: callback(cb)
{ }

And then you could create a button using std::bind:

Button* temp1 = new Button(std::bind(&OtherClass::LoadFile, this));
Button* temp2 = new Button(std::bind(&AnotherClass::SaveFile, this));

Now temp1->callback() will actually call LoadFile() on the instance of OtherClass that it was constructed with. Which is what you wanted. And, we can still use free functions:

void whatever();
Button* temp3 = new Button(whatever);

Insert the usual caveats about using raw pointers and new and preferring unique_ptr.




回答2:


You can't really have a pointer to a member function because it's meaningless w/o the hidden this pointer on which the object is invoked.

There are a few common solutions to the problem you're trying to solve.

The C way:

Store a void* in addition to the function pointer. Then pass the void* int the callback. This is less "safe", but is common in C.

OOP way #1:

Subclass Button and have a virtual function on the base class, say onPress that can be implemented in the subclasses.

OOP way #2:

Have a callback interface independent of Button that your custom classes implement.

EDIT: Or use lambdas as mentioned in comments. I am still re-learning C++ the modern way myself.



来源:https://stackoverflow.com/questions/31255235/passing-a-member-function-as-an-argument-to-a-constructor

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!