实现回调与指向非静态成员函数的指针 [英] Implementing callback with pointer to non-static member function

查看:171
本文介绍了实现回调与指向非静态成员函数的指针的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

假设我正在开发杂货列表管理器。我有一个窗口与 GroceryListDisplay ,这是一个控件,显示在杂货列表上的项目。杂货数据由程序的Model组件存储在 GroceryStorage 类中。

Let's say I'm developing a grocery list manager. I have a window with a GroceryListDisplay, which is a control that displays the items that are on the grocery list. The grocery data is stored by the Model component of the program, in the GroceryStorage class.

要加载保存的文件插入我的程序,我的程序的模型组件必须重新填充从该文件导入的数据。 View组件需要通知这个新数据,否则GUI不会被更新,用户看不到导入的数据。

To load a saved file into my program, the Model component of my program has to be repopulated with data that was imported from the file. The View component will need to be notified of this new data, otherwise the GUI won't be updated and the user can't see the imported data.

这里的概念

/* 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始终保持最新。

When the user instructs the GUI to import a certain file, the View will call a function in the Model that loads data from that given file. Then, the repopulateFromModel function is called to get the GUI up-to-date.

GroceryStorage :: sendGroceryItemsToGUI 中为回调使用函数指针的麻烦,因为否则Model将必须知道应该调用View中的哪个函数,这将是一个违反的模型/视图原理。

I'm going through the trouble of using a function pointer for the callback in GroceryStorage::sendGroceryItemsToGUI because otherwise the Model would have to know which function in the View it should call, which would be a violation of the Model/View principle.

这个代码块有一个大问题。如果我在现实生活中使用这个概念,我得到一个类似于

There's one big issue with this block of code. If I use this concept in a real life situation, I get a compiler error that says something similar to


错误: (GroceryListDisplay ::)(std :: string)'不匹配'void(*)(std :: string)'

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

编译器是否要求我硬编码函数指针来源的类的名称?我不能这样做,因为这意味着该模型知道哪个View类负责处理回调,而这又是一个模型/视图违反。

Is the compiler asking me to hardcode the name of the class from which the function pointer originates? I can't do that, because this would imply that the Model knows which View class is responsible for handling the callback which, again, would be a Model/View violation.

推荐答案

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

The best thing to do is to abstract away from using raw function pointers. There are two usual approaches:

第一种是使用 std :: bind + std :: function (或他们的 boost :: 对应的旧编译器缺少 std :: std :: tr1 :: 实现):

The first is to use std::bind + std::function (or their boost:: counterparts on older compilers lacking std:: or std::tr1:: implementations):

#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 ,所以 std :: string / code>按价值只是愚蠢的99%的时间,但这不是一个严格必要的步骤。)

(Note that I've changed addItemToList to take the std::string by const& because passing a std::string by value is just silly 99% of the time, but this wasn't a strictly necessary step.)

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

The second is to make sendGroceryItemsToGUI a function template rather than taking a 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;
};

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

The latter approach will always be more efficient, but is sometimes impractical/undesirable due to the fact that function templates must always be defined in header files.

这篇关于实现回调与指向非静态成员函数的指针的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆