4

次のコードを機能させるにはどうすればよいですか? メンバーを静的にすることはできません。親は子について知らず、ブーストへのアクセス権がありません。仮想関数を使用しない理由は、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);
};

編集:多くの答えがありました。私にとって最も効果的なものは、もちろんc ++ 11に依存する消去、std::bind、およびstd::functionというキーワードを使用します。完全にコンパイル可能な例を次に示します。

#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

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 に答える
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 に答える
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)、子オブジェクトと親オブジェクトが実際に何をするかによって異なります。

于 2013-04-25T09:28:36.370 に答える
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 は、クラスが多重継承または仮想継承を使用するかどうかに応じて、メンバーへのポインターが異なる方法で表現されるこの「メンバーポインターの最小限の表現」を思いついたからです。したがって、子が多重継承を使用し、親が使用しない場合、多重継承を持つクラスのメンバーへのポインターが単一継承のクラスのメンバーへのポインターに適合しないため、キャストを実行できません。常にジェネリック形式を使用するコンパイラ オプションがありますが、それを使用してコンパイルしたコードと使用せずにコンパイルされたコードは、バイナリ互換性がありません。

于 2013-04-25T09:58:45.483 に答える
0

宣言に関しては、あなたは逃げることができます

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

もちろん、それはそれらのポインタをどのように格納するかという問題を解決しません。1つの一般的な解決策は

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

もちろん、これは型消去のもう 1 つの形式です。

于 2013-04-25T09:44:25.400 に答える
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 に答える