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

时间:2013-04-25 09:17:12

标签: c++

如何使以下代码有效?我不能让成员保持静态,父母不知道Child,我无法获得提升。我不使用虚函数的原因是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);
};

编辑: 有很多答案。最适合我的那些使用关键字erasure,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;
}

7 个答案:

答案 0 :(得分:4)

您需要某种形式的擦除类型。假设您不能使用任何已经存在的复杂的(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函数。但是,如果我在没有测试的情况下编写代码,我可能会弄乱一些代码......

答案 1 :(得分:4)

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));
    }

    ... 

答案 2 :(得分:2)

关于如何解决传递函数指针的大量建议 - 你可以稍微重新设计 - 并正确使用继承......

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...

这种设计看起来不是更清晰吗?显然这个建议是基于你在那里发布的简单片段,如果你的架构更复杂,那么它就是不同的故事......

答案 3 :(得分:0)

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

可能更好的是定义static Child Child::loadPNG(string filename),但这取决于Child和Parent对象的确实做什么。

答案 4 :(得分:0)

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);
    }
};

答案 5 :(得分:0)

宣言明智,你可以逃脱

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);
  }
};

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

答案 6 :(得分:0)

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 ++是一种编译器,它实际上在所有情况下都可以工作。因为Microsoft提出了“成员指针的最小表示”,其中指向成员的指针的表示方式不同,具体取决于类是使用多重继承还是虚拟继承。因此,如果子进程使用多重继承,但父进程没有,则无法进行强制转换,因为指向具有多重继承的类成员的指针不适合指向具有单继承的类成员的指针。有一个编译器选项总是使用通用格式,但使用和不使用它编译的代码是二进制不兼容的。

相关问题