Passing a member function as an argument to a cons

2019-04-13 03:56发布

问题:

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.