4

如何使以下代码工作?我不能让成员成为静态的,父母不知道孩子,我也无法获得提升。我不使用虚函数的原因是子类应该能够定义 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;
}
4

7 回答 7

4

您需要某种形式的类型擦除。假设您不能使用任何已经存在的复杂的(boost::function, std::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我认为您可以使用带有指向成员模板参数的函数模板来摆脱这些函数。但是如果我在没有测试的情况下编写它,我可能会弄乱那个代码......

于 2013-04-25T09:34:11.023 回答
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));
    }

    ... 
于 2013-04-25T09:41:04.817 回答
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...

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

于 2013-04-25T10:08:10.753 回答
0

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

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

于 2013-04-25T09:28:36.370 回答
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);
    }
};
于 2013-04-25T09:34:19.230 回答
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);
  }
};

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

于 2013-04-25T09:44:25.400 回答
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++ 是编译器,它实际上并不适用于所有情况。因为微软提出了这种“成员指针的最小表示”,其中指向成员的指针表示不同,具体取决于类是否使用多重继承或虚拟继承。因此,如果子级使用多重继承,而父级没有,则无法进行强制转换,因为指向具有多重继承的类成员的指针不适合指向具有单继承的类成员的指针。有一个编译器选项可以始终使用通用格式,但是使用和不使用它编译的代码是二进制不兼容的。

于 2013-04-25T09:58:45.483 回答