gpt4 book ai didi

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

转载 作者:太空狗 更新时间:2023-10-29 21:29:07 25 4
gpt4 key购买 nike

假设我正在开发一个购物 list 管理器。我有一个带有 GroceryListDisplay 的窗口,它是一个显示杂货 list 上的项目的控件。杂货店数据由程序的模型组件存储在 GroceryStorage 类中。

要将保存的文件加载到我的程序中,必须使用从该文件导入的数据重新填充我程序的模型组件。需要将此新数据通知 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 导入某个文件时, View 将调用模型中的函数从该给定文件加载数据。然后,调用 repopulateFromModel 函数来更新 GUI。

我遇到了在 GroceryStorage::sendGroceryItemsToGUI 中使用回调函数指针的麻烦,否则模型必须知道它应该调用 View 中的哪个函数,这将是违反了模型/ View 原则。

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

error: argument of type ‘void (GroceryListDisplay::)(std::string)’ does not match ‘void (*)(std::string)’

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

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

最佳答案

最好的做法是从使用原始函数指针中抽象出来。有两种常用的方法:

第一个是使用 std::bind + std::function(或者他们的 boost:: 对应的旧编译器缺少 std::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 更改为通过 const& 获取 std::string,因为传递了一个 std::在 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;
};

后一种方法总是更有效,但有时不切实际/不可取,因为函数模板必须始终在头文件中定义。

关于c++ - 使用指向非静态成员函数的指针实现回调,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/5556773/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com