【问题标题】: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;
}

【问题讨论】:

  • 请不要在问题中添加“问题已解决”。而是将解决您的问题的答案标记为已接受。

标签: c++ class vector initialization construct


【解决方案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*/) : callback{function} {
          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;
      }
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2012-08-17
        • 1970-01-01
        • 1970-01-01
        • 2011-08-04
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多