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

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(使用前将#替换为@)

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

  • 使用 ADAL v3 使用 ClientID 对 Dynamics 365 进行身份验证

    我正在尝试对我们的在线 Dynamics CRM 进行身份验证以使用可用的 API 我能找到的唯一关于执行此操作的官方文档是 https learn microsoft com en us dynamics365 customer enga
  • 关于逻辑/算法的想法以及如何防止线程写入 Sql Server 中的竞争

    我有以下逻辑 public void InQueueTable DataTable Table int incomingRows Table Rows Count if incomingRows gt RowsThreshold async
  • EntityHydrate 任务失败

    我最近安装了 Visual Studio 11 Beta 和 Visual Studio 2010 之后 我无法在 Visual Studio 2010 中构建依赖于 PostSharp 的项目 因此我卸载了 Visual Studio 1
  • C# 中的 Stack<> 实现

    我最近一直在实现递归目录搜索实现 并且使用堆栈来跟踪路径元素 当我使用 string Join 连接路径元素时 我发现它们被颠倒了 当我调试该方法时 我查看了堆栈 发现堆栈内部数组中的元素本身是相反的 即最近 Push 的元素位于内部数组的
  • 在 Mono 中反序列化 JSON 数据

    使用 Monodroid 时 是否有一种简单的方法可以将简单的 JSON 字符串反序列化为 NET 对象 System Json 只提供序列化 不提供反序列化 我尝试过的各种第三方库都会导致 Mono Monodroid 出现问题 谢谢 f
  • 2个对象,完全相同(除了命名空间)c#

    我正在使用第三方的一组网络服务 但遇到了一个小障碍 在我手动创建将每个属性从源复制到目标的方法之前 我想我应该在这里寻求更好的解决方案 我有 2 个对象 一个是 Customer CustomerParty 类型 另一个是 Appointm
  • 如何向 Mono.ZeroConf 注册服务?

    我正在尝试测试 ZeroConf 示例http www mono project com Mono Zeroconf http www mono project com Mono Zeroconf 我正在运行 OpenSuse 11 和 M
  • C# 根据当前日期传递日期时间值

    我正在尝试根据 sql server 中的两个日期获取记录 Select from table where CreatedDate between StartDate and EndDate我通过了5 12 2010 and 5 12 20
  • 测量进程消耗的 CPU 时钟

    我用 C 语言编写了一个程序 它是作为研究结果创建的程序 我想计算程序消耗的确切 CPU 周期 精确的循环次数 知道我怎样才能找到它吗 The valgrind tool cachegrind valgrind tool cachegrin
  • LinkLabel 无下划线 - Compact Framework

    我正在使用 Microsoft Compact Framework 开发 Windows CE 应用程序 我必须使用 LinkLabel 它必须是白色且没有下划线 因此 在设计器中 我将字体颜色修改为白色 并在字体对话框中取消选中 下划线
  • 如何在 Javascript 中连接 C# ActiveX 事件处理程序

    我尝试使用几个代码片段将 ActiveX 对象与 Javascript 事件处理程序挂钩 我无法确定为什么事件处理程序没有被调用 带有项目的 Github 存储库 https github com JesseKPhillips Csharp
  • 让网络摄像头在 OpenCV 中工作

    我正在尝试让我的网络摄像头在 Windows 7 64 位中的 OpenCV 版本 2 2 中捕获视频 但是 我遇到了一些困难 OpenCV 附带的示例二进制文件都无法检测到我的网络摄像头 最近我发现这篇文章表明答案在于重新编译一个文件 o
  • 如何从 Boost.PropertyTree 复制子树

    我有一些boost property tree ptree 我需要树来删除一些具有特定标签名称的元素 例如 xml 表示源ptree如下
  • 使用 gcc 时在头文件中查找定义的好方法是什么?

    在使用 gcc 时 有人有推荐的方法在头文件中查找定义吗 使用 MSVC 时 我只需右键单击并选择 转到定义 这非常好 我使用过 netbeans gcc 它确实有代码帮助 包括到定义的超链接 所以这是一种选择 但是 我想知道是否有任何其他
  • C++ 指针引用混淆

    struct leaf int data leaf l leaf r struct leaf p void tree findparent int n int found leaf parent 这是 BST 的一段代码 我想问一下 为什么
  • C:设置变量范围内所有位的最有效方法

    让我们来int举个例子 int SetBitWithinRange const unsigned from const unsigned to To be implemented SetBitWithinRange应该返回一个int其中所有
  • Streamwriter 覆盖 txt 文件中的文本

    有没有什么方法可以重新打开流写入器而不创建新的写入对象 因为此时 当调用 WriteOdd 时 streamwriter 正在覆盖在它之前调用的 WriteEven public void WriteEven StreamWriter wr
  • 如果将变量设置为等于新对象,旧对象会发生什么?

    假设我们有一个 X 类not有一个超载的operator 功能 class X int n X n 0 X int n n n int main X a 1 an object gets constructed here more code
  • 声明一个负长度的数组

    当创建负长度数组时 C 中会发生什么 例如 int n 35 int testArray n for int i 0 i lt 10 i testArray i i 1 这段代码将编译 并且启用 Wall 时不会出现警告 并且似乎您可以分配
  • 如果找不到指定的图像文件,显示默认图像的最佳方式?

    我有一个普通的电子商务应用程序 我将 ITEM IMAGE NAME 存储在数据库中 有时经理会拼错图像名称 为了避免 丢失图像 IE 中的红色 X 每次显示产品列表时 我都会检查服务器中是否有与该产品相关的图像 如果该文件不存在 我会将其

随机推荐