在c++中将指向成员函数的指针传递给父函数

Passing pointer to member function to parent in c++

本文关键字:函数 指针 成员 c++      更新时间:2023-10-16

如何使以下代码正常工作?我不能让成员静态,Parent不知道Child,我也无法获得boost。我不使用虚拟函数的原因是Child类应该能够定义1-N处理程序。

class Parent
{
public:
    void registerFileHandler(string ext, memFuncPtr);
};
class Child : public Parent
{
    Child()
    {
        registerFileHandler("jpg", &Child::loadJpg);
        registerFileHandler("png", &Child::loadPNG);
    }
    void loadJpg(string filename);
    void loadPNG(string filename);
};

编辑:答案很多。最适合我的是使用关键字擦除、std::bind和std::function,它们当然依赖于c++11。以下是一个完整的可编译示例:

#include <string>
#include <map>
#include <iostream>
#include <functional>
using namespace std;
class Parent
{
public:
    void load(string filename)
    {
        // See if we can find a handler based on the extension.
        for(auto it = handlers.begin();it!=handlers.end();it++)
            if(filename.substr(filename.size()-it->first.size(), it->first.size())==it->first)
                it->second(filename);
    }
    template<typename Class>
    void registerFileHandler(Class* p, void (Class::*func)(string), string ext)
    {
        using namespace std::placeholders; //for _1, _2, _3...
        handlers[ext] = std::bind(func, p, _1);
    }
private:
    map<string, std::function<void(string)> > handlers;
};
class Child : public Parent
{
public:
    Child()
    {
        registerFileHandler(this, &Child::loadJpg, "jpg");
        registerFileHandler(this, &Child::loadPNG, "png");
    }
    void loadJpg(string filename)
    {
        cout << "loading the jpeg "<< filename << endl;
    }
    void loadPNG(string filename)
    {
        cout << "loading the png "<< filename << endl;
    }
};

int main(int argc, char* argv[])
{
    Child child;
    child.load("blah.jpg");
    child.load("blah.png");
    return 0;
}

std::functionstd::bind:怎么样

class Parent
{
public:
    void registerFileHandler(string ext, const std::function<void(string)> &f)
    {
    }
};
class Child : public Parent
{
public:
    Child()
    {
        using namespace std::placeholders; //for _1, _2, _3...
        registerFileHandler("jpg", std::bind(&Child::loadJpg, this, _1));
        registerFileHandler("png", std::bind(&Child::loadPNG, this, _1));
    }
    ... 

您需要某种形式的类型擦除。假设你不能使用任何已经存在的复杂的(boost::functionstd::function(,那么你可以推出自己的:

class MemFuncPtr {
    void *obj;
    void (*caller)(void*, string);
  public:
    MemFuncPtr(void *obj, void(*caller)(void*, string)) : obj(obj), caller(caller) {}
    void operator()(string filename) {
        caller(obj, filename);
    }
};
class Child : public Parent
{
    Child()
    {
        registerFileHandler("jpg", MemFuncPtr(this, &jpgcaller));
        registerFileHandler("png", MemFuncPtr(this, &pgncaller));
    }
    void loadJpg(string filename);
    void loadPNG(string filename);
  private:
    static void jpgcaller(void *obj, string filename) {
        static_cast<Child*>(obj)->loadJpg(filename);
    }
    static void pngcaller(void *obj, string filename) {
        static_cast<Child*>(obj)->loadPng(filename);
    }
};

我认为您可以使用带有指向成员模板参数的指针的函数模板来消除那些static函数。但如果我在没有测试的情况下编写代码,我可能会把它弄得一团糟…

关于如何传递函数指针(您可能需要稍微重新设计(以及正确使用继承,有很多建议。。

class FileLoader
{
  virtual void load() = 0; // real load function
};
class LoadManager
{
  // Here is your registry
  std::map<std::string, std::uniqe_ptr<FileLoader>> _loaders;
};
class JpegLoader : public FileLoader
{
};
class BitmapLoader : public FileLoader
{
};
// etc.
// Now register these with the LoadManager and use from there...

这个设计看起来是不是更清晰了一点?显然,这个建议是基于你在那里发布的简单片段,如果你的架构更复杂,那就另当别论了。。。

要使代码正常工作,registerFileHandler()应该向将加载图像的实际子对象传递一个指针。

最好定义static Child Child::loadPNG(string filename),但这取决于Child和Parent对象的实际作用

class Child;  // unfortunately this is needed
class Parent
{
public:
    void registerFileHandler(string ext, void (Child::*mem_fn) (string), Child* obj) {
                                       // ^^^^the member func  ^^^^^^^^  //the object to be used 
         (obj->*mem_fn)("test");
    }
};

class Child : public Parent
{
    Child()
    {
        registerFileHandler("jpg", &Child::loadJpg, this);
        registerFileHandler("png", &Child::loadPNG, this);
                                                            // You need to pass this.
    }
    void loadJpg(string filename);
    void loadPNG(string filename);
};

然而,你所做的可能有些过头了。你为什么不能用这样的东西?

class Parent
{
public:
    virtual void registerFileHandler(string ext)  = 0;
};

class Child : public Parent
{
    Child()
    {
        registerFileHandler("jpg");
        registerFileHandler("png");
    }
    void loadJpg(string filename);
    void loadPNG(string filename);
    virtual void registerFileHandler(string ext)   {
        loadJpg(ext);
        loadJpg(ext);
    }
};

声明明智,你可以逃脱

class Parent
{
public:
    template <typename Child>
    void registerFileHandler(string ext, void (Child::*memFuncPtr)(string filename));
};

当然,这并不能解决如何存储这些指针的问题。一种常见的解决方案是

struct Handler {
   virtual ~Handler() { };
  virtual void Load(Parent*, std::string) = 0;
};
template<typename Child>
struct ChildHandler {
  void (Child::*memFuncPtr)(string filename);
  virtual void Load(Parent* p, string filename) {
      dynamic_cast<Child&>(*p).*memFuncPtr(filename);
  }
};

这当然是另一种类型擦除形式。

class Parent
{
public:
    // so we don't need to static-cast at the call site
    template <typename ChildT>
    void registerFileHandler(string ext, void (ChildT::*func)(string)) {
         doRegisterFileHandler(ext, static_cast<void (Parent::*func)(string)>(func));
    }
    void doRegisterFileHandler(string ext, void (Parent::*func)(string));
};
class Child : public Parent
{
    Child()
    {
        registerFileHandler("jpg", &Child::loadJpg);
        registerFileHandler("png", &Child::loadPNG);
    }
    void loadJpg(string filename);
    void loadPNG(string filename);
};

你照常调用函数:

this->*func(string)

在父级中。

规范说这应该有效(C++03中的5.2.9/9,C++11中的5.2.9/12(,MFC在消息映射中使用它。然而,Microsoft Visual C++是一个编译器,它实际上在所有情况下都能而不是工作。因为微软提出了这种"成员指针的最小表示法",根据类是否使用多重继承或虚拟继承,指向成员的指针的表示方式不同。因此,如果子级使用多重继承,而父级不使用,则无法执行强制转换,因为指向具有多重继承的类的成员的指针不适合指向具有单一继承的类成员的指针。编译器可以选择始终使用泛型格式,但使用和不使用泛型格式编译的代码是二进制不兼容的。