尝试为成员进行回调

时间:2018-07-12 11:59:42

标签: c++ pointers sfml

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

我还有一个类Button,该类在其构造函数中使用类型为fptr的变量,我有一个函数makeButton()返回一个Button对象。

stickyNotes还有一个名为rendermainWindow的成员,它呈现主窗口并添加一个按钮,我试图创建一个类型为fptr的新变量,该变量设置为以下地址stickyNotes::addNote,但出现错误:

  

'&':对绑定成员函数表达式的非法操作

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成为静态。并且我想使其保持公开状态,如何使用addNote的回调函数创建按钮? 如果有解决方法,我会很高兴听到它。

2 个答案:

答案 0 :(得分:1)

问题:

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

解决方案1:

简便的方法是创建一个接口,在您的类中实现它,然后返回或分配this作为接口。

解决方案2:

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

解决方案3:

如@GoswinvonBrederlow在评论中所建议,请使用lambda并将其返回或分配为std::function

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

答案 1 :(得分:1)

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

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

这两个在本质上是矛盾的:如果您的按钮带有简单的函数指针,则不能将其用于调用成员函数(不包括诸如全局状态之类的可怕骇客)。

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

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

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

最后,您可以允许用户将一些“自定义数据”传递给按钮界面,可能以void*std::any的形式传递给回调。然后用户可以存储例如该自定义数据中的stickyNotes实例。这与简单的函数指针兼容。但是,此方法绕过了类型安全性,并且级别很低。它在某些库中使用,因为它很通用,但是比起std::function方法来说,它更令人讨厌。