簡體   English   中英

在c ++中將指向成員函數的指針傳遞給parent

[英]Passing pointer to member function to parent in 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;
}

你需要某種形式的類型擦除。 假設您不能使用任何已經存在的復雜的( 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函數。 但是,如果我在沒有測試的情況下編寫代碼,我可能會把代碼弄得一團糟......

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

    ... 

關於如何處理函數指針的大量建議 - 你可能會稍微重新設計 - 並正確使用繼承..

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在消息映射中使用它。 然而,微軟的Visual C ++的編譯器在哪里,它實際上在所有情況下都不起作用 因為Microsoft提出了“成員指針的最小表示”,其中指向成員的指針的表示方式不同,具體取決於類是使用多重繼承還是虛擬繼承。 因此,如果子進程使用多重繼承,但父進程沒有,則無法進行強制轉換,因為指向具有多重繼承的類成員的指針不適合指向具有單繼承的類成員的指針。 有一個編譯器選項可以始終使用通用格式,但使用和不使用它編譯的代碼是二進制不兼容的。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM