带向量的函数指针

Posted

技术标签:

【中文标题】带向量的函数指针【英文标题】:Function Pointer With Vector 【发布时间】:2020-07-29 10:36:06 【问题描述】:

我是 C++ 新手。我正在尝试用 OpenGL 做可点击的按钮。我为每个按钮添加回调函数工作了 2 天,我尝试了很多我找到的方法,但我不能这样做。我下面的代码给出了内存错误。我的错在哪里?

ma​​in.h

#include <vector>
class Button 
public:
    // Storage Vector
    static std::vector<Button> Buttons;

    // typedef Function 
    typedef void (*pointerFunction)();

    // Constructor
    Button(/*Parameters*/);

    // Setting Callback
    void setCallBack(void(*function)());

    // Callback pointer
    pointerFunction callback;

    int value 4 ;
;

ma​​in.cpp

#include <iostream>
#include "main.h"

std::vector<Button> Button::Buttons;

Button::Button(/*Parameters*/) 
    // ...
    Button::Buttons.push_back(*this);


void Button::setCallBack(void(*function)()) 
    this->callback = function;

    this->callback(); // Here is work!


void testFunction() 
    std::cout << "Test\n";


void createMember() 
    Button classMember;
    classMember.setCallBack(&testFunction);


int main() 
    createMember();

    for (Button& button : Button::Buttons) 
        std::cout << button.value; // I can access this value.
        button.callback(); // But here is give memory error!
    

    return 0;

【问题讨论】:

请不要在问题中添加“问题已解决”。而是将解决您的问题的答案标记为已接受。 【参考方案1】:

在这个函数中

void createMember() 
    Button classMember;
    classMember.setCallBack(&testFunction);

有两件事正在做。第一个是创建本地对象classMember。被调用的 construcfor 将对象的副本推送到向量 Buttons 内。副本的数据成员callback未初始化。

.local 对象classMember 的数据成员callback 在其副本被推送到向量后被初始化。

重写函数至少像

void createMember() 
    Button classMember;
    Button::Buttons.back().setCallBack(&testFunction);

如果未显式提供相应的初始化程序,则应特别使用文字 nullptr 初始化所有数据成员。在这种情况下,您将能够检查指针类型的数据成员是否等于nullptr 或存储实际值。

【讨论】:

【参考方案2】:

您的 createMember 函数没有按预期工作。

void createMember() 
    Button classMember;
    classMember.setCallBack(&testFunction);

创建一个将在函数退出时销毁的本地对象。

你可以这样做(虽然我认为这不是一个好的解决方案。)

Button & createMember() 
    static Button classMember;
    classMember.setCallBack(&testFunction);
    return classMemeber;

更好的解决方案:

std::vector<Button> Button::Buttons;

int main() 

    Button b;

    for (Button& button : Button::Buttons) 
        button.setCallBack(testFunction);
        std::cout << button.value; // I can access this value.
        button.callback(); // But here is give memory error!
    

    return 0;

请注意,您必须在某处玷污Button::Buttons,因为它是static 成员这在您的代码中是正确的,我忽略了它。

而且,要添加至少一个Button,您必须创建一个 以添加到vector

输出:

Test

4Test

您在setCallBack 和循环中调用了两次testFunction。 (我添加了一个换行符。)

如果正如createMember 函数名所暗示的那样,你想调用它来创建每个新元素,你可以在构造函数中传递函数指针。如果它可以像您的示例一样简单地复制(类中没有指针或资源分配),您只需创建实例,vector 副本就可以了。

Button::Button(pointerFunction f) : callback (f) 
    // ...
    Button::Buttons.push_back(*this);


void createMember() 
    Button classMember (testFunction);


int main() 
    createMember ();

    for (Button& button : Button::Buttons) 
        std::cout << button.value; // I can access this value.
        button.callback(); // But here is give memory error!
    

不过,我认为这对于任何真实事物来说都不是一个好的设计。

【讨论】:

【参考方案3】:

你的错误是你创建了一个本地对象,把它的副本push到vector中,把回调地址放到原来的对象上,然后销毁原来的对象。好吧,你可以把回调地址作为构造函数参数,然后副本就有了。

// Constructor
Button(void(*function)(), /*Parameters*/) : callbackfunction 
    Button::Buttons.push_back(*this);

但我建议向 Button 类添加一个静态函数,该函数负责创建 Button 对象并返回对它的引用。这也消除了不必要的临时对象的创建/删除。

#include <iostream>
#include <vector>

class Button 
public:
    // Storage Vector
    static std::vector<Button> Buttons;

    // typedef Function 
    typedef void (*pointerFunction)();

    // Constructor
    Button(/*Parameters*/);

    // Setting Callback
    void setCallBack(void(*function)());

    // Callback pointer
    pointerFunction callback;

    template<class... U>
    static Button& createButton(U&&... u) 
        return Buttons.emplace_back(std::forward<U>(u)...);
    

    int value 4 ;
;

std::vector<Button> Button::Buttons;

Button::Button(/*Parameters*/) 
    // ...
    Button::Buttons.push_back(*this);


void Button::setCallBack(void(*function)()) 
    this->callback = function;

    this->callback(); // Here is work!


void testFunction() 
    std::cout << "Test\n";


void createMember() 
    auto &classMember = Button::createButton(/**/);
    //Button classMember;
    classMember.setCallBack(&testFunction);


int main() 
    createMember();

    for (Button& button : Button::Buttons) 
        std::cout << button.value; 
        button.callback();
    

    return 0;

【讨论】:

以上是关于带向量的函数指针的主要内容,如果未能解决你的问题,请参考以下文章

如何防止从非常量访问指针中过滤指针向量的函数?

在函数中使用向量指针时出错

用于清除指针向量的模板函数

通过指针在函数之间传递向量

指针向量的复制构造函数

存储各种 void 函数指针及其参数的向量