试图为会员回拨

Trying to make a callback for a member

我有 class stickyNotes,其中有函数 addNote,它是一个 public 非静态函数。 在代码中定义了一个新类型: typedef void(*fptr)

我还有 class Button ,它在其构造函数中接受类型为 fptr 的变量,我有一个函数 makeButton() returns a Button 对象。

stickyNotes 还有另一个名为 rendermainWindow 的成员,它呈现主 window 并添加一个按钮,我正在尝试创建一个 fptr 类型的新变量设置为 stickyNotes::addNote 的地址,我收到错误:

'&': illegal operation on bound member function expression

stickyNotes::rendermainWindow:

void stickyNotes::rendermainWindow() {
    /*
    Renders the main window
    */
    this->buttonList.empty(); // buttonList is a list of all buttons
    mainWindow->clear(sf::Color(29, 29, 27, 255)); // clearing the window
    sf::Vector2u windowDimensions = mainWindow->getSize(); // getting window dimensions
    fptr cb = &this->addNote; <-------- ERROR HERE 
    Button plusB = makeButton((int)(windowDimensions.x * 0.75),
                              (int)(windowDimensions.y * 0.15),
                              (int)(windowDimensions.x * 0.85),
                              (int)(windowDimensions.y * 0.25),
                              mainWindow,
                              cb);
    // first button to add stuff
    std::vector<Button> vect;
    vect.push_back(plusB);
    this->buttonList.push_back(vect);
    renderNotes();

}

我尝试过的:

this->addNote 替换为 stickyNotes::addNote

PS:

我不想让 addNote 成为静态的。我想保留它public,如何制作一个带有addNote回调函数的按钮? 如果有解决方法,我将很高兴听到。

问题:

成员函数只是 class 命名空间中的一个偏移量,因此您不能将偏移量作为绝对地址。

解决方案 1:

简单的方法是创建一个接口,在您的 class 中实现它,然后 return 或分配 this 作为接口。

解决方案 2:

其他选项是创建指向成员函数的指针。 例如:void (Class::*pfunc)() 将声明 pfunc 指针,它可以指向 class Class 的成员函数,如 void Class::Member()

解决方案 3:

正如@GoswinvonBrederlow 在评论中所建议的那样,使用 lambda 和 return 或将其分配为 std::function:

std::function<void ()> Class::f()
{
    return []() { /* do something...*/ };
}

您正在尝试将回调传递给按钮。如果您要求回调是一个简单的函数指针(不接收任何参数),那么您的回调不能保留任何 state.

但是,所有非静态成员函数都需要至少一个状态才能被调用:它们需要知道要使用哪个对象in/with。

这两者从根本上是不一致的:如果您的按钮采用简单的函数指针,则它不能用于调用成员函数(抛开全局状态等可怕的技巧)。

解决这个问题的一种方法是使用成员函数指针:它们指定它们在哪个 class 上运行(即是其成员函数)并且必须在该 class 的实例上调用。由于您可能希望按钮回调可用于多个 class,因此您可以使用多态性 - 这是 SHR 建议的界面解决方案。

按钮界面的另一个选项是获取(并存储)一个 std::function<void()> 而不是您的函数指针。 std::function 可以存储状态,与 lambda 函数一起使用非常方便:

Button plusB = makeButton(/* ... */, [this]() { addNote(); });

最后,您可以允许用户将一些 "custom data" 传递给按钮界面,可能作为传递给回调的 void*std::any。然后用户可以存储例如该自定义数据中的 stickyNotes 实例。这与简单的函数指针兼容。但是,这种方法规避了类型安全并且非常低级。它在某些库中使用,因为它非常通用,但使用起来比 std::function 方法更烦人。