使用指向非静态成员函数的指针实现回调

2024-01-20

假设我正在开发一个杂货清单管理器。我有一扇窗户,上面有GroceryListDisplay,这是一个显示购物清单上的商品的控件。杂货数据由程序的模型组件存储在GroceryStorage class.

要将保存的文件加载到我的程序中,必须使用从该文件导入的数据重新填充我的程序的模型组件。 View 组件需要收到此新数据的通知,否则 GUI 将不会更新并且用户无法看到导入的数据。

这是我为促进这一点而提出的概念。

/* A View class that represents a GUI control that displays the grocery list */
class GroceryListDisplay {
public:
  void repopulateFromModel(GroceryStorage* gs) {
    this->gs = gs;

    /* Delete every list entry that was loaded into GUI */
    this->clearList();

    /* Import grocery list from the Model */
    void (*itemAdder)(std::string) = addItemToList;
    this->gs->sendGroceryItemsToGUI(addItemToList);
  }

  void addItemToList(std::string);
  void clearList();
private:
  GroceryStorage* gs;
}

/* A Model class that stores the grocery list */
class GroceryStorage {
public:
  void sendGroceryItemsToGUI(void (*itemAdder)(std::string)) {
    /* Sends all stored items to the GUI */
    for (int i = 0; i < (int)this->groceryItems.size(); ++i)
      itemAdder(this->groceryItems[i]);
  }
private:
  std::vector<std::string> groceryItems;
}

当用户指示 GUI 导入某个文件时,视图将调用模型中的函数,从该给定文件加载数据。然后,repopulateFromModel调用函数以使 GUI 保持最新状态。

我正在经历使用函数指针进行回调的麻烦GroceryStorage::sendGroceryItemsToGUI因为否则模型必须知道它应该调用视图中的哪个函数,这将违反模型/视图原则。

这段代码有一个大问题。如果我在现实生活中使用这个概念,我会收到一个编译器错误,内容类似于

错误:“void (GroceryListDisplay::)(std::string)”类型的参数与“void (*)(std::string)”不匹配

编译器是否要求我对函数指针所源自的类的名称进行硬编码?我不能这样做,因为这意味着模型知道哪个视图类负责处理回调,这又将是模型/视图违规。

我是否误解了函数指针的工作原理?


最好的办法是抽象化,不再使用原始函数指针。通常有两种方法:

第一个是使用std::bind + std::function(或他们的boost::旧编译器上的对应项缺乏std:: or std::tr1::实现):

#include <functional>
#include <vector>
#include <string>

class GroceryStorage {
public:
    void sendGroceryItemsToGUI(std::function<void(std::string const&)> const& itemAdder) {
        for (groceryItems_t::const_iterator iter = groceryItems.begin(), iter_end = groceryItems.end(); iter != iter_end; ++iter)
            itemAdder(*iter);
    }

private:
    typedef std::vector<std::string> groceryItems_t;
    groceryItems_t groceryItems;
};

class GroceryListDisplay {
public:
    void repopulateFromModel(GroceryStorage* const gs_) {
        gs = gs_;
        clearList();
        gs_->sendGroceryItemsToGUI(std::bind(&GroceryListDisplay::addItemToList, this, std::placeholders::_1));
    }

    void addItemToList(std::string const&);
    void clearList();

private:
    GroceryStorage* gs;
};

(请注意,我已经改变了addItemToList采取std::string by const&因为通过一个std::stringby value 在 99% 的情况下都是愚蠢的,但这并不是严格必要的步骤。)

第二个是要使sendGroceryItemsToGUI一个函数模板而不是采用std::function:

#include <functional>
#include <vector>
#include <string>

class GroceryStorage {
public:
    template<typename F>
    void sendGroceryItemsToGUI(F const& itemAdder) {
        for (groceryItems_t::const_iterator iter = groceryItems.begin(), iter_end = groceryItems.end(); iter != iter_end; ++iter)
            itemAdder(*iter);
    }

private:
    typedef std::vector<std::string> groceryItems_t;
    groceryItems_t groceryItems;
};

class GroceryListDisplay {
public:
    void repopulateFromModel(GroceryStorage* const gs_) {
        gs = gs_;
        clearList();
        gs_->sendGroceryItemsToGUI(std::bind(&GroceryListDisplay::addItemToList, this, std::placeholders::_1));
    }

    void addItemToList(std::string const&);
    void clearList();

private:
    GroceryStorage* gs;
};

后一种方法将always更有效,但有时不切实际/不合需要,因为函数模板必须始终在头文件中定义。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

使用指向非静态成员函数的指针实现回调 的相关文章

  • 使用遗留代码(使用reinterpret_cast)真的是一种很好的技术吗?

    下面的代码来自一篇关于C 面试问题的帖子here https www toptal com c plus plus interview questions 我从来不知道这种技术 尽管它声称是一种很好的技术 我的问题是 什么情况下需要使用它
  • MVVM:来自 FileOpenPicker 的图像绑定源

    我将 OnActivated 添加到 app xaml cs 中 它可以正常工作 protected async override void OnActivated IActivatedEventArgs args var continua
  • 处理器关联组 C#

    我使用的是 72 核的 Windows Server 2016 我看到有两组处理器 我的 net 应用程序将使用一个或其他组 我需要能够强制我的应用程序使用我选择的组 我看到下面的代码示例 但我无法使其工作 我可能传递了错误的变量 我希望应
  • 为类型列表创建别名并将其作为模板参数传递

    我正在使用可变参数模板来实现访问者模式 template
  • 如何配置 Ninject 来注入 NodaTime IClock

    在我的 NinjectConfigurator 中我有 container Bind
  • 如何在 ASP.NET MVC 中处理会话数据

    假设我想存储一个名为language id在会议中 我想我也许可以做如下的事情 public class CountryController Controller WebMethod EnableSession true AcceptVer
  • 如何反序列化 XML 文档

    如何反序列化此 XML 文档
  • 如何在 Google Mock 中使用可选参数来模拟方法?

    如何使用可选参数模拟方法谷歌模拟 例如 class A public void set enable bool enabled true class MockA public A MOCK METHOD1 set enable void b
  • 图片框、双击和单击事件

    我有一个奇怪的问题 我有一个图片框双击事件以及单击事件 问题是即使我双击该控件 也会引发单击事件 如果我禁用单击事件 则双击事件正在工作 这个问题已经在这里讨论过 https stackoverflow com questions 1830
  • 使用c#在mac上启动外部进程

    我成功地使用 System Diagnostics Process Start 在 Windows 上启动我的外部单声道可执行文件 然而在mac上却失败了 我没有收到任何错误 只是什么也没发生 我尝试按以下方式进行操作 System Dia
  • 在编译输出中添加程序集绑定 (app.config)

    如果我编译应用程序 则会在输出中自动添加程序集绑定 具体的程序集绑定不在app config在 Visual Studio 中但在创建的应用程序配置中 有什么办法可以检查为什么会自动添加程序集绑定吗 选项AutoGenerateBindin
  • C++:避免​​在重载中将字符串自动转换为布尔值

    我想创建一组方法 这些方法将根据其类型输出具有特殊格式的值 当我这样做时 到目前为止看起来还不错 static void printValue std ostringstream out int value out lt lt value
  • 仅使用一个 #include 表达式一次包含多个头文件?

    是否有任何表达式可以使语法一次包含多个标头 而无需为每个新文件编写 include 表达式 例如 include
  • 将两个垂直滚动条相互绑定

    我在控件中有两个 TextBox 并且它们都有两个 VerticalScrollBar 我想在它们之间绑定 VerticalScrollBars 如果一个向上 第二个也会向上等等 如果可以的话我该怎么做 Thanks 不是真正的绑定 但它有
  • Qt 多重继承和信号

    由于 QObject 我在 QT 中遇到了有关多重继承的问题 我知道很多人也有同样的问题 但我不知道该如何解决 class NavigatableItem public QObject Q OBJECT signals void desel
  • 批量插入,asp.net

    我需要获取与会员相对应的 ID 号列表 在任何给定时间处理的数量可能在 10 到 10 000 之间 我可以毫无问题地收集数据 解析数据并将其加载到 DataTable 或任何内容 C 中 但我想在数据库中执行一些操作 将所有这些数据插入表
  • 括号内声明的对象的范围

    如果我声明一个这样的对象 void main myclass objectA anotherclass true true 0 即 我通过直接调用后者的构造函数来创建一个 objectA 和另一个对象 anotherclass anothe
  • 模板类中模板方法专门化的 clang 自动返回类型错误?

    试图理解另一个问题 https stackoverflow com questions 38054055 clang fails to compile template function with auto return type insi
  • 在 C++/CLI 中传递非托管指针

    我正在创建一个依赖于众多 C 静态库的 C CLI 包装器 DLL 一些函数调用需要传入非托管指针 我如何正确地传递它们 此外 其他函数期望 this 指针 作为 void 传入 传递 这个 的正确方法是什么 这是我的班级定义 public
  • 获取线段上最接近另一个点的点[关闭]

    Closed 这个问题需要调试细节 help minimal reproducible example 目前不接受答案 我想找到线段AB上最接近另一个点P的点 我的想法是 Get a1 and b1由直线公式y1 a1x b1 使用 A 点

随机推荐