2

すべてが同じ一般的な構文と基本クラスを持つさまざまなクラスからいくつかのメンバー関数を呼び出せるようにしたいと考えています。の線に沿った何か

class A: public BaseClass
{
public:
    A();
    ~A();

    int DoFoo();
    int DoBar();
    int DoBarBar();
};

class B : public BaseClass
{
public:
    B();
    ~B();

    int DoSomething();
    int DoSomethingElse();
    int DoAnother();
};

両方のクラスのメンバー関数を 1 つのマップに潜在的に配置して、次のようなものを作成できる場所

 key           value
"Option1"     *DoFoo()
"Option2"     *DoSomething()
"Option3"     *DoFoo()
 ...            ...
"Option6"     *DoAnother()

関数が属するクラスに関係なく、選択したオプションに基づいて値を返す関数を呼び出すことができる場所。

いくつかの検索を通じて、私は独自のマップされたファンクターのセットを実装しようとしました。ただし、マップはファンクタのアドレスを保持しますが、内部の関数は null になります。

クラスオブジェクトと関数ポインターを格納するファンクター宣言は次のとおりです

    #include <stdio.h>
    #include <vector>
    #include <algorithm>
    #include <map>
    #include <string>

//////////////////////////////////////////////////////////////
//Functor Classes
////////////////////////////////////////////////////////////// 
    class TFunctor
    {
    public:
      virtual void operator()()=0;  // call using operator
      virtual int Call()=0;        // call using function
    };


    // derived template class
    template <class TClass> class TSpecificFunctor : public TFunctor
    {
    private:
      int (TClass::*fpt)();   // pointer to member function
      TClass* pt2Object;                  // pointer to object

    public:

      // constructor - takes pointer to an object and pointer to a member and stores
      // them in two private variables
      TSpecificFunctor(TClass* _pt2Object, int(TClass::*_fpt)())
         { pt2Object = _pt2Object;  fpt=_fpt; };

      // override operator "()"
      virtual void operator()()
       { (*pt2Object.*fpt)();};              // execute member function

      // override function "Call"
      virtual int Call()
        {return (*pt2Object.*fpt)();};             // execute member function
    };

    typedef std::map<std::string, TFunctor*> TestMap;

//////////////////////////////////////////////////////////////
//Test Classes
//////////////////////////////////////////////////////////////
//Base Test class 
class base
{
public:
    base(int length, int width){m_length = length; m_width = width;}
    virtual ~base(){}


    int area(){return m_length*m_width;}

    int m_length;
    int m_width;
};

//Inherited class which contains two functions I would like to point to
class inherit:public base
{
public:
    inherit(int length, int width, int height);
    ~inherit();
    int volume(){return base::area()*m_height;}
    int area2(){return m_width*m_height;}

    int m_height;
    TestMap m_map;
};

私の継承クラスのコンストラクターは次のようになります。

inherit::inherit(int length, int width, int height):base(length, width)
{
    m_height = height;
    TSpecificFunctor<inherit> funcA(this, &inherit::volume);
    m_map["a"] = &funcA;

    TSpecificFunctor<inherit> funcB(this, &inherit::area2);
    m_map["b"] = &funcB;
}

これは、2 つの関数をマップにマッピングする場所です。上記の関数では、メモリ アドレスと関数ポインタに関しては問題ないように見えます。

次に、新しいクラスで継承のインスタンスを作成しようとします...

class overall
{
public:
    overall();
    ~overall(){}

    inherit *m_inherit;
    TestMap m_mapOverall;
};
overall::overall()
{
    m_inherit = new inherit(3,4,5);

    TestMap tempMap = m_inherit->m_map;
    int i = 0;
}

ここで m_inherit->m_map の値を見ると、キーはまだ一貫していることがわかりますが、ポイントしようとした関数のメモリ アドレスは消えています。

ファンクターの経験はあまりありませんが、私の理解では、状態を保持できます。これは、クラス外でメンバー関数を呼び出すことができることを意味すると思います。しかし、範囲外なのでメンバー関数が消えると思い始めています。

4

2 に答える 2

2

そうです、スクープの問題です。inheritコンストラクターでは、とfuncAfuncB両方ともスタックに割り当てられ、関数がスコープ外になると破棄されます。m_map古いポインタを持つ葉。

あなたが本当に欲しいのは次のようなものです

inherit::inherit(int lenght, int width, int height) :base(length, width)
{
    m_height = height;

    // allocated on the heap
    TSpecificFunctor<inherit> * funcA = new TSpecificFunctor<inherit>(this, &inherit::volume);
    m_map["a"] = funcA;

    // allocated on the heap
    TSpecificFunctor<inherit> * funcB = new TSpecificFunctor<inherit>(this, &inherit::area2);
    m_map["b"] = funcB;
} // when this function exits funcA and funcB are not destroyed

ただし、メモリリークを避けるために、デストラクタinheritは値をクリーンアップする必要があります

inherit::~inherit()
{
    for(TestMap::iterator it = m_map.begin(); it != m_map.end(); ++it) {
        delete it->second;
    }
}

newandを使用するとdelete、メモリ リークが発生しやすくなります。std::unique_ptrそれらを防ぐために、や などのスマート ポイントを調べることをお勧めしstd::shared_ptrます。また、C++11 でのラムダの導入により、ファンクターは時代遅れになりつつあります。あなたがそれらに慣れていない場合、それらは本当にきちんとしていて、調べる価値があります.


コンパイラがそれらをサポートしている場合は、ラムダでこれを行う

#include <functional>

// ...

typedef std::map<std::string, std::function<int(void)>> TestMap;

// ...

inherit::inherit(int length, int width, int height):base(length, width)
{
    m_height = height;
    m_map["a"] = [this]() -> int { return volume(); };
    m_map["b"] = [this]() -> int { return area2(); };

    // can be called like so
    m_map["a"]();
    m_map["b"]();
}

// No need to clean up in destructors
于 2012-07-11T01:03:43.633 に答える
1

その通りです。TSpecificFunctors は のコンストラクターの最後でスコープ外にinheritなるため、それらへのポインターを保持するべきではありません。

可能であれば、スマートポインターを優先します。

#include <memory>

...

typedef std::map<std::string, std::shared_ptr<TFunctor>> TestMap;

...

inherit::inherit(int length, int width, int height):base(length, width)
{
    m_height = height;
    m_map["a"] = std::shared_ptr<TSpecificFunctor<inherit>>(
        new TSpecificFunctor<inherit>(this, &inherit::volume));
    m_map["b"] = std::shared_ptr<TSpecificFunctor<inherit>>(
        new TSpecificFunctor<inherit>(this, &inherit::area2));
}

その場合の主な関心事は、が破棄さm_inherit->m_mapれた後に のファンクターが呼び出されないようにすることです。そうしm_inheritないと、未定義の動作が発生します。この場合、リークしているので安全ですm_inherit(決して破壊されることはありません)。

于 2012-07-11T01:04:16.363 に答える